VXCTF 2nd Writeup

目錄

  1. Crypto 2000: Good Old Days 1-2
  2. Web 1000: DNA-Box


Crypto 2000: Good Old Days 1-2

0x00. 背景

第一個版本的 source code 如下,第二個版本大同小異,只是提供了更少的資料。

不過不要緊,我們要的資訊就只有 highlight 了的那些:

是的,就只需要 ciphertext 就夠了。

首先說明一下整個程序是怎樣運作:

Key generation:

  • 生成一個約 256 bits 的質數 \(p\)
  • 生成兩個亂數 \(2 \leq a, b < p\) 使得 \(a + b > p\)

加密 (\(m\rightarrow c\)):

  • 在 message 前後各自加上最多 1000 個字 (ASCII 不限)
  • 把所有字元一個一個字傳換成 ASCII code (\(m = m_1 m_2 … m_k\)、\(0 \leq m_i < 256\))
  • 逐個算出 \(c_i \equiv a m_i + b (\text{mod }p)\)

0x01. 思路與解法

利申:我用的是旁門左道,如果想知道 intended solution 的話,請找 harrier。

我的旁門左道的條件比較苛刻:

  1. 加入雜訊後的 plaintext 需要有 ASCII code 為 0x00, 0x01, 0x02, …,  0xFF
  2. \(p \leq 2a + b < 2p\)

如果第一個條件成立的話,我們可以枚舉 0x00、0x01 及 0x02 各自對應的 ciphertext。設它們為 \(c_0\)、\(c_1\) 及 \(c_2\),那麼\(c_0 \equiv b (\text{mod }p)\)、\(c_1 \equiv a + b (\text{mod }p)\)、\(c_2 \equiv 2a + b (\text{mod }p)\)。

由於 \(a < p\) 及 \(p < a + b < 2p\) ,我們有 \(c_0 = b\) 及 \(c_1 = a + b – p\)。

加上 \(p \leq 2a + b < 2p\) 這個條件的話,我們同時也有 \(c_2 = 2a + b – p\)。

對於每個枚舉的 \(c_0, c_1, c_2\),我們可以得出 \(a = c_2 – c_1\),\(b = c_0\) 及 \(p = c_0 + c_2 – 2 c_1\)。

然後我們要做的事就是看看\(3a + b (\text{mod }p)\)、\(4a + b (\text{mod }p)\)、…、\(255a + b (\text{mod }p)\) 在不在 ciphertext space 裡面。如果在的話,我們幾乎可以肯定 \(a, b, p\) 就是題目生成的那組。

最後就是寫一個 lookup table 找回對應的 plaintext。


Web 1000: DNA-Box

0x00. 背景

DNA-Box 是 Ozetta 出的一道題目,這裡可以打包下載

題目有兩個主要的功能:

  1. 上載一個檔案
  2. include 一個檔案

0x01. 思路與解法

如果上面的兩個功能都沒有限制的話,那就是 baby steps 等級了:

  • 上載一個 PHP: <?=`$_GET['a']`;
  • Include 那個 PHP

然而難度在於條件:

  1. 上載的檔案不能超過 64KB,而且 ATCGatcg 以外的字元會被刪除。
  2. Include 的格式為  include($payload . ".inc.php");

所以如果上載剛剛那個 web shell 的話,你只會看到 Ta。那要怎麼辦呢?首先看一下基礎知識 (?)。

基礎知識 (1):Remote Code Execution (RCE)

PHP 的 include 函數除了可以 include path names 外,還可以用它內置的 wrapper -  php://

舉個例子, include("php://filter/convert.base64-encode/resource=a.php"); 就是把 a.php 以 base64 表示。我們可以經過 base64 decode 來查看 a.php 的源代碼。

又或者,如果 b.txt(是的,你用其他 extension 也可以)的內容如下:

那麼  include("php://filter/convert.base64-decode/resource=b.txt"); 則會出現以下東西:

可以 decode 兩次嗎?可以: include("php://filter/convert.base64-decode|convert.base64-decode/resource=b.txt");

然後輸出一片空白 - 那是因為內容會被 decode 成  <?php eval($_GET['trolled']); ?>,然後就 execute 起來了。

把網址改成 ?trolled=echo%20"Hello%20World!";,網頁會顯示 “Hello World!”:

把變數 trolled 換成 system('ls'); 就跟在 command line 輸入 ls  一樣 - 這意味著我們已經得到 web shell。

以下的 payload 為  ?trolled=echo%20%27<xmp>%27;%20system(%27ls%20-al%27);,但是可以改成其他東西的。

基礎知識 (2):Base64

這個我知道!

這個我就不詳細講了。

就講一下重點:PHP 的 base64 decode filter 會無視所有非 base64 的字元。如果把  Ok... 8-p 丟去 decode 的話,結果會跟 decode  Ok8p 時一樣。(試試看吧!)

正題

說了這麼多廢話,進入開始正題了:如何使用 DNA 來拼出 shell 呢?

要知道得到 base64 得天下,因為 base64 decode 就是 ASCII 了。

ATCGatcg  Base64 Charset  ASCII

那麼先定下一個目標 - 我們先向收集 base64 的字元出發:

過了一個回合之後,我們可以拼到 [1] 014678adfghijkmpqrBFLMNSZ+ 。

用以上字元繼續可以得到 [2] 0123456789abcdefghijkmnotuvwxzABCDEFGHIJKLMNOPQRSTVWXYZ+/=

要怎樣拼出 Y 呢? AAtCAAtGAGtAAAtC 可以 decode 成  BFkB,而 BFkB 可以 decode 成  B

用以上字元再拼一次,即可得到 base64 的所有字元。

ATCGatcg → [1][2] → Base64 Charset → ASCII

由於每一個箭頭代表一次 base64 decode - 把以下的內容 decode 四次後就可得到 shell。

你問我如何寫出上面的 payload?這是練習題。


發表迴響