記一次偶然的CTF經歷


注:本文中所有題面數據均經過處理,並未復現現場數據,僅作輔助理解過程之用。

0x00 前言

一次偶然,我經歷了一場奇異的CTF解題過程,盡管上下折騰許久,但終有所收獲。現將解題過程與思路分享。

0x01 開場

開題 github.com/xxxx8888/AaBbcCdD    打開后只有一個README

5KB才6行?Raw加載原始文件

一串神秘字符和一段熟悉的base64編碼,由編碼中的等號發現,是由兩段base64組成的。

先看第一段神秘字符串,乍一看摸不着頭腦。仔細看看,有見 :等字符,大膽猜測這是url的變形。

對半拆分后看到熟悉的字眼,發現是字符的移位變換。

hts/x_lj3w33lgic.eiwn_e
tp:/xsfhta25.lthm/_atky

按上到下,左到右的規則重新拼合得到鏈接

https://xx_slfjh3twa3235l.glitch.me/i_want_key

打開后得到一組RSA公鑰,且先保存。

-----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANE6WKCgLluv3Fn5kqg9HR+XwJ6iattQ
CmqV/xEOxJU9HVUl30hoVvRspz1a5rQ8xiZhueWRhNMkdkmyO18itPkCAwEAAQ==
-----END PUBLIC KEY-----

再解析base64字符串,第一組得

經過了解,這是一種叫 jjencode 的混淆算法,通過在線工具解密得到又一組base64,解密得亂碼。想到可能與RSA解密相關。暫存

看方才第二組編碼 繼續解碼得到兩個鏈接

dive.giggle.cow/file/d/26TTOq3zCg-2nrBdeTWtV7tHgg4z-xxxXx/view?usp=sharing
s1.ax1x.com/2000/01/01/bCZFqK.png

分別是一張gif →   (文件名:This_is_d.gif)  與一張二維碼圖片。等等,二維碼好像不對勁?

通過查閱資料了解到,二維碼(QR Code)的的關鍵部位就在三個基准點和黑白相間的基准線上

其中黑白相間的基准線立刻引起了我的注意。在對畸形的二維碼進行分析后,發現左半側的圖像經過了旋轉變換

我通過python腳本來修復二維碼(Photoshop,GIMP等工具亦可)

 1 from PIL import Image
 2 
 3 img_origin = Image.open('qr.png')
 4 img = img_origin.copy()
 5 img_origin.close()
 6 width = img.width
 7 height = img.height
 8 left_img = img.crop((6,6,width//2-10,height-6))
 9 left_img = left_img.rotate(180)
10 img.paste(left_img,(6,6))
11 img.show()

得到原圖 通過在線工具得出其中內容,還是base64

*******************************************************************
U2FsdGVkX18I1TdtQoyW2bd02ewoSz+5Zo4c3Tit1r3mA0Z+jwMw9uhwIR6o+kN/
nK2pbsHjs8U5CqN9oKC6wg==
*******************************************************************

解析 得到Salted__開頭的加密字符串,根據經驗,判斷這是aes加密的字符串,保存。

順利解決的部分就告一段落。總結整理,得到一張閃動的gif,一組RSA公鑰以及對應的密文,一組aes密文。

0x02 困境

前半段告結。剩下的數據令人有些摸不着頭腦。黑白閃動的圖片?難道是摩爾斯電碼?

使用identify工具查看各幀的顯示時間,剔除重復項后發現間隔長短不一,從0.1-1.1s不等。

而查閱資料得摩爾斯電碼的時間點最多只有1,3,5,7四種。此路不通。只能換個任務思考。

RSA密文怎么解密?我可只有公鑰,而RSA在應用場景中只有私鑰才能解密(理論中公鑰與私鑰是可以交換的)。

我先想到了針對RSA的攻擊。先通過openssl查看公鑰的$n$,$e$。

 唔,512bit不好暴力拆解n,抱着試試看的心理,上傳到factordb,出現意想不到的一幕

???一個合法的$n$竟然無法分解?再仔細看,發現是機(jiǎo)智(huá)的出題人故意上傳了無解的結果來欺騙我們。。

一條能走的路都沒有了,我只好一邊用yafu跑着暴力分解,一邊苦苦思索解題思路。

0x03 突破

終於,發現提示就近在眼前!RSA中的私鑰$d$,莫非就是This_is_d.gif所指?

黑白閃動,不就是01信號嘛!幀長度就是行程!立刻動手,使用python解析隱藏的二進制數據。

 1 from PIL import Image
 2 
 3 img = Image.open('This_is_d.gif')
 4 frame = 1
 5 img.seek(img.tell())
 6 
 7 try:
 8     while True:
 9         rgb_img = img.convert('RGB')
10         color = rgb_img.getpixel((5, 5))
11         duration = img.info['duration']
12         if color == (255, 255, 255):
13             state = '1'
14         else:
15             state = '0'
16         print(state * (duration//100),end='')
17         img.seek(img.tell() + 1)
18         frame += 1
19 except EOFError:
20     pass

二進制數據轉16進制得

得到了$d$,又有$n$,根據RSA解密公式 $c^{d}\equiv m\pmod N$,編寫python腳本

 1 import base64
 2 import binascii
 3 
 4 n = 0xd13a58a0a02e5bafdc59f992a83d1d1f97c09ea26adb500a6a95ff110ec4953d1d5525df486856f46ca73d5ae6b43cc62661b9e59184d3247649b23b5f22b4f9
 5 d = 0x63cd1e0b787cc4756d7969a7c0226eaaec3b10304f224fdab81ed66d2f8b2bd7ff4010a5f498faf01a32139ac371c4a32fddc3bcac47ad53914eee70ea4bb651
 6 with open('task2_base64.txt') as f:
 7     str_base64 = f.read()
 8 encrypted = int(binascii.b2a_hex(base64.b64decode(str_base64)),16)
 9 plain = hex(pow(encrypted,d,n))[2:]
10 padding_end = plain.find('00')+2 #--!-!--注意 RSA PKCS#1 padding--!-!--
11 str_plain = binascii.a2b_hex(plain[padding_end:])
12 print(str_plain)

Succeed ! 解密得到字符串 839$nc*!9vx_ ,很有可能就是aes密文的密碼。使用openssl工具進行解密成功。得到一組字符串。

大寫字母,2個以上的=號,莫不是base編碼家族中的base32?通過在線工具解析, 最終拿到flag。

0x04 總結

總的來看,這道題難度一般,綜合了Crypto,Misc等知識點,又通過“此地無銀三百兩”的手法設置障礙。本次解題過程前半部分一切順利,失誤就在於缺乏對提示的敏感性(其實是太菜),導致進入了錯誤的解題方向,需要我整理思考。

0x05 后記

本次解題過程中使用的幾種工具與python模塊:

    factordb [http://www.factordb.com/index.php]

    Pillow [https://pypi.org/project/Pillow/]

    identify [https://imagemagick.org/script/identify.php]

    openssl [https://github.com/openssl/openssl]

我再推薦幾種常用的、本文中未使用的工具與python模塊:

    rsatool [https://github.com/ius/rsatool]

    gmpy2 [https://pypi.org/project/gmpy2/]

    yafu [https://sourceforge.net/projects/yafu/]

    pycrypto [https://pypi.org/project/pycrypto/]

具體安裝及用法則不再贅述。

0xFF 題外話

在本題RSA解密中,由於加密前明文采用PKCS#1標准進行padding,我在編寫腳本解密時未考慮到,導致解密不出明文。

通過查閱規范文本【RFC 8017】了解該標准下的解密方式后解密成功,下附規范文本中的解密步驟:

7.2.2.  Decryption Operation
   RSAES-PKCS1-V1_5-DECRYPT (K, C)
   Input:
      K        recipient's RSA private key
      C        ciphertext to be decrypted, an octet string of length k,
               where k is the length in octets of the RSA modulus n

Moriarty, et al.              Informational                    [Page 29]
RFC 8017                      PKCS #1 v2.2                 November 2016

   Output:
      M        message, an octet string of length at most k - 11
   Error:  "decryption error"
   Steps:
      1.  Length checking: If the length of the ciphertext C is not k
          octets (or if k < 11), output "decryption error" and stop.
      2.  RSA decryption:
          a.  Convert the ciphertext C to an integer ciphertext
              representative c (see Section 4.2):
                 c = OS2IP (C).
          b.  Apply the RSADP decryption primitive (Section 5.1.2) to
              the RSA private key (n, d) and the ciphertext
              representative c to produce an integer message
              representative m:
                 m = RSADP ((n, d), c).
              If RSADP outputs "ciphertext representative out of range"
              (meaning that c >= n), output "decryption error" and stop.
          c.  Convert the message representative m to an encoded message
              EM of length k octets (see Section 4.1):
                 EM = I2OSP (m, k).
      3.  EME-PKCS1-v1_5 decoding: Separate the encoded message EM into
          an octet string PS consisting of nonzero octets and a message
          M as
             EM = 0x00 || 0x02 || PS || 0x00 || M.
          If the first octet of EM does not have hexadecimal value 0x00,
          if the second octet of EM does not have hexadecimal value
          0x02, if there is no octet with hexadecimal value 0x00 to
          separate PS from M, or if the length of PS is less than 8
          octets, output "decryption error" and stop.  (See the note
          below.)
      4.  Output M.

另外,我們只要具有一定密碼學基礎,在已經得到$d$的情況下,就可以展開$d$泄漏攻擊,還原出完整的pem私鑰。

首先當$d$泄露之后,我們自然可以解密所有加密的消息。我們甚至還可以對模數$N$進行分解。其基本原理如下

我們知道$ed \equiv 1 \bmod \varphi(n)$,那么存在一個$k$使得

$ed-1=k\varphi(n)$

又$\forall a\in {Z}_n^*$,滿足$a^{ed-1}\equiv1(\bmod n)$。令

$ed-1=2^st$

其中,$t$是一個奇數。然后可以證明對於至少一半的$a\in {Z}_n^*$,存在一個$i\in[1,s]$,使得

$a^{2^{i-1}t}\not\equiv\pm1(\bmod n),a^{2^{i}t}\equiv1(\bmod n)$

成立。如果$a$,$i$滿足上述條件,$gcd(a^{2^{i-1}t}-1,n)$是$n$的一個非平凡因子,所以可以對$n$進行暴力分解。

下面使用rsatool得到私鑰,並用openssl直接解密密文。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM