2014年澳大利亞資訊保安挑戰 CySCA CTF 官方write up Crypto篇

wyzsk發表於2020-08-19
作者: F1uYu4n · 2014/07/23 10:36

from:https://www.cyberchallenge.com.au/CySCA2014_Crypto.pdf

0x00 背景


Fortcerts開發了不少涉及加密演算法的程式並已經自行測試了這些程式,現在他們想要了解其他人對於這些加密程式安全性的看法。

0x01 標準銀河字母(Standard Galactic Alphabet)


Question 請對Fortcerts自定義加密演算法的Slightly Secure Shell程式進行白盒測試。找出漏洞並證明其可以利用來獲取機密資訊。伺服器執行在172.16.1.20:12433

Source

#!python
a580fd052a2f1ef9a0753ee36ad6bd51-crypt01.py
=== snip... ===
def execute_command(command,plain,coded):
    print "Running command: bash %s" % command
    proc = subprocess.Popen(("/bin/bash","-c",command),stdout=subprocess.PIPE,stderr=subprocess.PIPE)
    proc.wait()
    stdoutdata = proc.stdout.read()
    stdoutdata += proc.stderr.read()
    output = ""
    for letter in stdoutdata:
        if letter in plain:
            output += coded[plain language=".find(letter)"][/plain]
        else:
            output += letter

    return output

def handle_client(conn,addr):
    plain = "`1234567890-=~!@#$%^&*()_+[]\{}|;':\",./<>?abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ "
    conn.send("You have connected to the Slightly Secure Shell server of Fortress Certifications.\n")
    coded = shuffle_plain(plain)
    command = ""
    conn.send("#>")

    while 1:
        data = conn.recv(1)
        if not data: break
        if data == "\x7f" or data == "\x08":
            if len(command) > 0:
                command = command[:-1]
            continue
        if data == "\n" or data =="\r":
            if len(command) == 0: continue
            conn.send("Running command: '%s'\n" % command)
            cmd_stdout = execute_command(command,plain,coded)
            conn.sendall(cmd_stdout+"\n")
            command = ""
            conn.sendall("Key reset\n")
            coded = shuffle_plain(plain)
            conn.sendall("#>")
        else:
            if data not in plain:
                continue
            command += data
        conn.sendall(data)
    conn.close()
=== snip... ===

Designed Solution 選手可以在自定義命令之前傳送一個echo命令來獲得加密金鑰並對輸出值進行破譯,然後使用多個命令來查詢包含key的檔案,並檢視其內容。 Write Up 首先閱讀系統提供的原始碼,明確程式讀取使用者的輸入值,直到它獲到一個換行符或回車符,然後透過bash來執行命令,並使用單碼代換對命令的執行結果加密後將輸出返回給使用者。在此之後,金鑰被重置。 我們可以透過管道命令來在單個金鑰的使用過程中執行多個命令,基於此來恢復金鑰和對輸出進行解密。 下面連線到伺服器來傳送一些命令以驗證我們的假設:可以執行多個命令並且金鑰僅在命令都執行完後被重置。

#!bash
#>nc 172.16.1.20 12433
You have connected to the Slightly Secure Shell server of Fortress Certifications.
#>echo AAAA
echo AAAARunning command: 'echo AAAA'
6666
Key reset
#>echo AAAA;echo AAAA
echo AAAA;echo AAAARunning command: 'echo AAAA;echo AAAA'
zzzz
zzzz
Key reset
#>echo AAAA;echo BBBB;echo ABAB
echo AAAA;echo BBBB; echo ABABRunning command: 'echo AAAA;echo BBBB; echo ABAB'
8888
5555
8585
Key reset

可以看出A和B總是被相同字元替換,因此可以確定這是一個單碼代換加密,同時可以得知金鑰是在所有命令的輸出結果顯示之後再更新。 我們透過兩個bash命令來執行任意命令,第一個echo明文字串,第二個執行自定義命令。這樣輸出結果的開始將是密文字元表,剩下的內容就是第二個命令的輸出。

#!bash
#>echo abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ; echo TestString
echo abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ; echo TestStringRunning command: 'echo abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ; echo estString'
_u;pSw"y|r^QFJ <Lk\+{KnEX$#%=UDb/6[HlBOaPRA54(-!'xq: <<< Ciphertext alphabet
(S\+4+k|J" <<< Test string

由此可以使用密文字元表來解密測試字串。 明文字元表 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ 密文字元表: _u;pSw"y|r^QFJ <Lk\+{KnEX$#%=UDb/6[HlBOaPRA54(-!'xq: 密文測試字串: (S\+4+k|J" 明文測試字串: TestString 這種方法看來行之有效,下面使用ls命令來定位flag,並用cat命令來顯示它。

#!bash
#>echo abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890.;ls -1
echo abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890.;ls -1Running command: 'echo abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890.;ls -1'
 qaF)-:"XzJ^#Ehp2H_*]iDMUP\@8`rNdIm6AW3&5e%0}[Rk/VYS>{tl<;KfG4n <<Key
qXE       << bin
F)i       << dev
)*a       << etc
-^ :n*M*  << flag.txt
^Xq       << lib
Key reset
#>echo abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890.; cat flag.txt
echo abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890.; cat flag.txtRunning command: 'echo
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890.; cat flag.txt'
q^0sE`"{G3KSo& @):nUZ=;+[BM?Lx*]CwIdmOg#2,VX-!Plav'e\9N4/_F(Wf7
Lq=Gq:?qKEs-{qoEW__ << CaviarBakedShame966

執行這些命令並對響應解碼後得到flag:CaviarBakedShame966  

0x02 壓縮會話(Compression Session)


Question 請對Fortcerts的高安全性金鑰生成伺服器進行白盒測試。找出能造成秘密資料洩漏的漏洞。伺服器執行在172.16.1.20:9999 Source 5a92cb8141992b7b71497a3bc920c7a5-crypt02.py

#!python
=== snip... ===
def compress(d):
    c = zlib.compressobj()
    return(c.compress(d) + c.flush(zlib.Z_SYNC_FLUSH)).encode("hex")

def encrypt(aes,d):
    return aes.encrypt(d).encode("hex")

def handle_client(conn, addr):
    AESKey = os.urandom(32)
    ctr = Counter.new(128)

    aes = AES.new(AESKey, AES.MODE_CTR, counter=ctr)

    BANNER = "\t\tWelcome to the Keygen server.\n\t\t"
    BANNER += "="*30
    BANNER += "\n[+]All access is monitored and any unauthorised access will be treated as CRIME\n"
    conn.send(BANNER)

    SECRET = 'Key:' + helpers.load_flag()
    data = conn.recv(2048).strip()

    while len(data):
        data = compress(SECRET + data)
        data = encrypt(aes, data)
        conn.send(data)
        data = conn.recv(2048).strip()
    conn.close()
=== snip... ===

Designed Solution 在這裡,選手使用被認為是犯罪的攻擊方式。由於在使用流密碼加密有關被洩漏的私鑰之前對資料進行了壓縮,選手可以自寫利用洩露資訊的指令碼 Write Up 首先分析系統提供的原始碼,伺服器把我們的輸入值,附加到金鑰後面並壓縮,然後加密,最後返回一個經過加密的16進位制編碼的字串。它使用了AES的CTR模式,說明這裡的AES是流密碼方式加密而不是分組加密。 使用netcat連線到遠端服務。標識表明這是一個金鑰伺服器,並警告說任何未經授權的訪問都將被視為是犯罪,這裡的CRIME特意用了大寫。伺服器既不提供任何提示來與使用者進行互動,也沒有提供help功能。

#!bash
#>nc 172.16.1.20 9999
        Welcome to the Keygen server.
        ==============================
[+]All access is monitored and any unauthorised access will be treated as CRIME

為了驗證在檢視原始碼片段時所得出有關程式碼功能的結論是否正確,下面傳送一些輸入到伺服器。從這裡我們可以看出,無論CTR模式中使用的計數器是怎麼增加的,每次提交之間的金鑰不會被重置,所以加密的資料不同。同時也能看出密文長度變化很小,這也表明使用的是流密碼而不是分組密碼。

AAAA
0a7a99948f447f19b22d7a9a74e25d591b8ff864fa0d3b80804f18b2798219cdb4429e39c8bd6594bc
50f640432fa7db9368db69ce769d2fe4ca30a1bc728c4d0ccdf701b8ae79000db82220
AAAA
ae387373a2131e25e26793aae4d3d1e61a74680c0c654dcef76ec0c27667bf861bd87d272f8d70e03f
b55a515be355f0030bae31beef06903ee6da654a96dbe41d134bea66d4993fbb414512
AAAA
7ff3513568e314e954bb872b604d3d9518fb64382fc6129d80465f27f99ab53a0300aea122627cc2fa
2d5d06600626079c20406f51fe9dcd9a680a9c9041c421308ddc322aac3b2f7dc9f253
AAAABBBB
b1217dacfc0e7927f3519baedb290a14b791b3b97c821158e84e956d071c887f97afd85213476712ff
da16fab035d54a72ee8f8608f94557e7e8866480aa5252eb1ed533c8d006c647824fc95123782b
AAAAAAAA
a3c7b8ae0f2628e126914284f62db7f60122daa23fc39cfc7f3cbd8999c2f979a3f8a214ebd225c48e
5ec5b303a672a45450ef13c00c110758e14d3f2981b95356c537f678357340be941877

注意到8個A加密的輸出長度和4個A一樣。與通常的不一樣的是,8個A一般會被壓縮成和4個A一樣的長度。被壓縮的資料透過流密碼加密,因此加密資料的長度等於被壓縮資料的長度。我們可以使用壓縮資料的長度來獲取資料被壓縮的有關資訊,這是一個有關CRIME攻擊的類似準則。你現在明白CRIME為什麼要大寫了嗎? 再次審查原始碼,我們明確了資料壓縮的方式。假設flag是“TestFlag”,輸入ABCD到伺服器,壓縮資料將是“Key:TestFlagABCD”。這個資料不會被壓縮,我們也得到了一個較長的響應字串。但是,如果輸入“Key:”,將要壓縮的資料就是“Key:TestFlagKey:”。當演算法壓縮資料時,它將得到兩個“Key:”,然後有效地壓縮並返回一個較短的響應。 我們傳送“Key:”來驗證推斷。每次傳送後都得到了不同的響應內容,但是響應的長度沒有變化。繼續傳送“Key:A”,響應的長度增加了;傳送“Key:B”和“Key:C”都得到了與“Key:A”相同的響應長度。當傳送“Key:D”給伺服器後,響應變短了,長度與傳送“Key:”的響應相同。

Key:
8bec8291d51787058bfe80c1303c3024f60b84812b4ec6a0d2a818dc74fc340bc45d99f89e29fbd7ee
c47f9091ebe518bd85b6e3d5c583719ee438ae4d597772afc9506aec2214865d9750ca
Key:A
964c86d2d100faaee0a920dee7716a29493cd3e11e7a33e1b0cfbe866e01f53208cab473a3eebc8607
dbf2ab25d197b6f6684c70f9a51b15debc4f282edf048cfb57c47b94e7b147b0f08fa4f27c
Key:B
85deee163f4e0982f67688fb0cdb7c735ccbe8f276796aadfdb7585a0528e5af9e107f5901637fdbc2
9c4622d821a91f4a4da9302115f601aeb97a120ffb86350797ed1e801a7e8c9f00f97e28dd
Key:C
af147c047eb3d0e84472cc95670919617ae4d10623b990ced591f074bae9d85f58bce66491ccc2a9c5
6ff9e438e78ce02f9c4d61086a5f52f16806a2eddc6d7daca5498c15ac854018a53491ab8d
Key:D
dd008730d0524e3af6332e4c69e69d7428968b7b21d32ebef9e35d8b908026b69023b09e2a9f978721
017b254b0b1b08ec7cd460a681e404eedb027c88e843f288ba5a15d276208aa86792b0

從這裡我們得知flag可能是以大寫的D開頭。知道了這個缺陷,就可以使用Python指令碼來連線伺服器,然後暴力破解flag的每一個字元。

#!python
import socket
import string
known_part_of_the_key = "Key:"
def Checker(s, bs):
    global known_part_of_the_key
    strings = string.letters + string.digits + "%/+=:"
    while 1:
        for ch in strings:
            s.send(known_part_of_the_key + ch)
            resp = s.recv(2048)
            if len(resp) == bs:
                print known_part_of_the_key
                known_part_of_the_key = known_part_of_the_key + ch

host = "172.16.1.20"
port = 9999
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((host, port))
banner = sock.recv(1024) #Receive the banner
print banner
sock.send(known_part_of_the_key)
resp = sock.recv(2048)
base_size = len(resp)
Checker(sock, base_size)

執行指令碼之後就得到flag:DrizzleVerandaFinger576

#!python
#>python soln.py
        Welcome to the Keygen server.
        ==============================
[+]All access is monitored and any unauthorised access will be treated as CRIME
Key:
Key:D
Key:Dr
Key:Dri
Key:Driz
****SNIP****
Key:DrizzleVerandaFinger576
****SNIP****
 

0x03 雜項(Chop Suey)


Question

Fortcerts的高管均表示反對使用白盒測試的方法,他們說真正的攻擊者不可能獲得原始碼的訪問權。請對Fortcerts的一個非常安全的加密服務進行黑盒測試。找出服務中能夠恢復加密資料的漏洞。這個非常安全的加密服務執行在172.16.1.20:1337

Designed Solution

選手需要分析伺服器以確定它是否使用了基於流密碼的演算法。一旦他們是這樣做的,需要確定是否使用了能產生重複金鑰流的IV。選手可以建立一個金鑰流的資料庫,然後透過重複的金鑰流來對flag進行解密。

Write Up

由於開始時沒有任何檔案提供,我們直接連線到加密服務看是否能明確它的功能。首先是一句“Key Reset!”的訊息,然後從標識處得知程式使用了非常安全的加密。另外還列出了兩個命令並告訴我們輸出的格式。

#!bash
#>nc 172.16.1.20 1337
Key Reset!
    Welcome to the FortCerts Certified Data Encryption Service
            This program uses very secure encryption
Commands:
E - Encrypt specified data
D - Dump service stored data
Output is in format <IV>:<Encrypted Data>

我們輸入這兩個命令來確定其功能。輸入E,得到一個Invalid use of encrypt,這說明該命令需要附帶引數。使用D命令時得到的字串表明加密結果與D命令下面的簡介資訊相符,最後得到一個“Key Reset!”的訊息。

E
Invalid use of encrypt. Usage E,<valuetoencrypt>
D
226e:01ef3044bac49aed75821296a10c060f0d0225386936
Key Reset!

依次輸入a到z,A到Z看是否存在未公開的命令。但是,除了E和D之外都顯示無效的命令,然後連線斷開。 我們嘗試給D和E命令附加一些引數來檢視其輸出是否有變化。 結果表明:無論給D命令什麼引數,儘管字串發生了變化,但是它的輸出格式不變。加密資料的長度以及IV的長度總是恆定的,同時最後總有一個“Key Reset!”訊息。

D
035b:3e4e9892b1ab053426dfa7168a0cc9827acf33d3480b
Key Reset!
D,
171f:73674b54f57f52874aed3027ece58d0f97780c88223e
Key Reset!
D,,,,
8e01:ba6b8ce2fa0e4d9f6b1888889df0110f4015b10a60fe
Key Reset!
D,AAAAA
a6ec:8f040a25951bdb727a974b7087c9cf733e44b427e2dd
Key Reset!
D,Test
b20f:0c823c92d80102913af7353254717d60b68f5e3d2a0e
Key Reset!
D
d4c0:b99815314cf8d5bec3d92be1204e66a044fc7050c48a
Key Reset!
D,BBBBBB
1026:bd9c781a6cd7d766040c0d539a13ebb9a590b1cebac1
Key Reset!

對於E命令,服務要求使用的正確命令格式為E,

相關文章