2020 KCTF秋季賽 | 第二題設計及解題思路

Editor發表於2020-11-23
在經過2天緊張激勵的比拼後,第二題歷時2天,最終落下帷幕,並於21日中午12點正式關閉攻擊通道。

2020 KCTF秋季賽 | 第二題設計及解題思路

此題共有4004人圍觀,並在開賽後的9小時左右,由崇文路大專戰隊奪下首旗,獲取一血額外加分。TLJ戰隊和dotsu_戰隊則位列第2、3位。截止賽題關閉,累計共有13支戰隊攻破成功!恭喜!



2020 KCTF秋季賽 | 第二題設計及解題思路


接下來讓我們一起來看一下這道題的設計思路和詳細解析吧。


一.題目簡介 


第二題:異常訊號


“破曉”已載著人類最後的希望駛向遠離地球的安全地帶。漂泊在廣袤無垠的宇宙中,人類今後該何去何從仍然是個問號。

時間一分一秒過去,“破曉”中央大廳的虛擬投射螢幕上,分析資料一行行變幻跳動著,懸浮的小視窗顯示著地球各地的實時影像。很多人簇擁在中央大廳裡屏息凝視著大熒幕,祈禱那一刻不要到來。

剎那間,警報聲劃破瞭如死一般的寂靜。“警告!警告!x-63892號黑洞已進入地球軌道。”

在放大的懸浮視窗上,可以清晰地看到原本晴空萬里的天幕上出現了一個突兀的黑點,裹挾著猛烈的風向地面席捲而來。伴隨著黑點的邊緣不斷向外擴散,並開始緩緩地逆時針旋轉,一個巨大的漩渦在空中形成,周圍的一切都逐漸開始變得模糊,越發看不清邊界,直到被漩渦中心強大的吸力捲入其中,與黑洞徹底融為一體。

黑洞貪婪地吞噬著地球上的一切,你和“破曉”上的倖存者們無助地看著來自世界各地的影像一個一個被黑暗所籠罩,伴隨著刺耳的電流聲和令人眼花的雪花屏,徹底失去訊號。

在宇宙空間站傳來的影像中,太陽系中那顆蔚藍的星球消失了……

“已無法檢測到來自地球的訊號……”系統一遍遍機械地重複著檢測結果,連綿不絕的嗚咽聲在大廳裡迴盪。

你盤腿坐在大廳一角,虛擬螢幕散發的藍光映照在你的臉上,忽明忽暗。看著自己的微型計算機也得出與“破曉”相同的檢測結論,你咬了咬牙,抬手把戴在頭上的衛衣帽子又往下扯了扯。

正當你死了那條心,打算關閉計算機時,紅光忽地亮了起來。定睛一看,你自制的訊號接收系統攔截到了一段稍縱即逝的異常訊號波動,追蹤來源竟發現訊號來自太陽系?!

訊號來自於一群圍繞太陽的新行星,每一顆行星與別的行星之間都有一個蟲洞,必須穿過每個蟲洞才能回到地球。

記住:蟲洞是奇異的,一旦穿過了曾經穿過的蟲洞,你將永遠迷失在次元空間中。


二. 出題團隊簡介


2020 KCTF秋季賽 | 第二題設計及解題思路


三. 設計思路


設計思路由作者 iiiiiiiiiiii 提供。


大家好!我們又見面了!這一次我們奉上的作品是:密室逃脫系列之——異常訊號。


演算法設計思路


要求給出一個列表a,其中包括不少於n=12個不同的非負整數,其最小值為0,最大值不大於w=89,使得任意兩個數之差的絕對值都不相同。

程式中過濾了對稱解。

在輸入上,通過設定random的種子構造了一個輸出為16bit的雜湊函式H;在第i位(每“位”為一個16bit的數字)上,若為H(i)則表示列表a中包含數字i;否則,若第i位為H(-i)則表示列表a中不包含數字i;否則,判定為輸入格式錯誤。



解題思路


要在[0, w]範圍內尋找n個數字,使其中任意2個數字之距離是不重複的。顯然,w越大/n越小,這個問題越容易解決。

但是此題限制了n≥12,w≤89。顯然破解者應該試圖在12/89的限制下去尋找解。更大的n和更小的w,都會使得求解更難。

然而在限制條件(12, 89)下遍歷所有可能,也是個天文數字。直接窮舉是不明智的。

於是,可以採用多種啟發式搜尋、截支、動態規劃等方法來加速。最終得到結果:此題是無解的!(cm中也已經對此給出了提示。這就要看你信不信了)

此題就如同天龍八部中的“珍瓏局”,眾多棋盤高手都無法破局,而唯獨虛竹,起手便將一片活棋下成死棋,而最終卻獲得了滿盤的勝利。

要破解此題,也需要這種“向死而生”的覺悟!

通過簡單分析,一般人直覺上會認為:更大的n和更小的w,都會使得求解更難。但此題設計巧妙之處正是:w為89是無解的,但w<89時卻未必。

如果你願意在89窮舉失敗之後,願意繼續嘗試88、87、86……也許你還願意繼續嘗試n=13、14、15……

你就會發現此題的解:
[0,2,6,24,29,40,43,55,68,75,76,85]
675E7A02004E86190D659330197C9F472513B036296BB8753E41C40C4A58D023566F7E5A6306E9516F1DF5687B340100884B0E1794621A2EA0792645AE04335CB9273F73C53E4B0AD1555821DE6C6438EA03704F647A7D660332D2170F4995141B60A12B2877AE42340EBA594025C6700E59D3075953DF1E656A705F7101F84C7E1804648A2F107B96461D12A35D2929AF743540BB0B4157C8224E6ED4390110E050661CED67E861F97EDC4A


但是,這並不是此題的解。

由於此題具有對稱性,所以還有一個對稱解;cm中過濾掉了對稱解的其中一個,所以下面這個才是正確序列號:
[0,9,10,17,30,42,45,56,61,79,83,85]
675E7A025B4786190D65933042199F472513AB5E312AB8753E41C40C4A58D023566FDD3A6306E9516F1DF5687B340100D3490E1794621A2EA0793450AD10335CB9273F73C53E4B0AD1555821DE6C64387111704FF61ADE2E0332897D0F4995141B60A12B2877AE42340EBA59402594244D3CD3075953DF1E656AEB357101F84C7E1804648A2F107BC44E1D12A35D2929AF743540BB0B193E93724E6ED4395A05E050661CED677333F97EDC4A


脫殼思路


老規矩,我們也安排了科銳的在讀學員利用所學對本題EXE加殼了。雖不敵看雪大佬,但也希望能拿出來給各位同行品鑑一下。下面是脫殼描述:


定位OEP


首先定位到程式的入口點:

2020 KCTF秋季賽 | 第二題設計及解題思路


使用工具進行dump:

2020 KCTF秋季賽 | 第二題設計及解題思路


修復IAT表和重定位表


定位到殼中的IAT表和重定位表的位置,根據其來修復脫殼後程式的重定位表和匯入表。


定位到重建後的IAT表的位置:

2020 KCTF秋季賽 | 第二題設計及解題思路


殼中填充完畢後的IAT表:

2020 KCTF秋季賽 | 第二題設計及解題思路


這裡的IAT表進行了加密:

2020 KCTF秋季賽 | 第二題設計及解題思路


定位到重定位表:

2020 KCTF秋季賽 | 第二題設計及解題思路


據此上資訊,修復IAT表和複製匯入表到dump後的程式即可。


修復原程式


修復後nop掉原程式中呼叫殼中函式的程式碼:

2020 KCTF秋季賽 | 第二題設計及解題思路


nop掉第二處:

2020 KCTF秋季賽 | 第二題設計及解題思路


處理完後,這裡就是加密演算法的執行流程了:

2020 KCTF秋季賽 | 第二題設計及解題思路


到此殼的保護機制完全去除,繼續進行加密演算法的破解即可。



四.解析過程


本解析過程由作者Anakin Stone 提供。

輸入處理

控制檯程式是通過NtReadFile獲取輸入的,不出意外的化,會有一個執行緒處於NtReadFile的同步等待狀態,依次檢視執行緒當前EIP,看是否有這樣的執行緒存在。

檢視執行緒列表如下:
2020 KCTF秋季賽 | 第二題設計及解題思路2020 KCTF秋季賽 | 第二題設計及解題思路

可以看到4號執行緒,就是我們要找的輸入執行緒,在777E1E6C下斷。輸入測試註冊碼12345678,回車,不出意外,成功斷下,觀察棧:
2020 KCTF秋季賽 | 第二題設計及解題思路

地址0xCC3C30存放的就是讀取到的我們輸入的測試註冊碼。對其下硬體讀斷點,跟蹤註冊碼處理。
保持對測試註冊碼的讀取跟蹤,直到下圖所示位置:
2020 KCTF秋季賽 | 第二題設計及解題思路

通過上下問分析,sub_561270會從註冊碼中依次取一個字元放到'引數2'所指地址,暫且將其命名為get_char,繼續跟蹤:
2020 KCTF秋季賽 | 第二題設計及解題思路

ebp-1dc記憶體資料如下:
2020 KCTF秋季賽 | 第二題設計及解題思路

可以看到,561410行-561454行程式碼,讀取驗證碼每一個字元直到'\a'(不包括),做對應的ASCII-數值轉換(從中也可以看出,是不支援小寫字元的), 存放到緩衝區 ebp-36c,執行完畢後,ebp-36c記憶體資料如下:
2020 KCTF秋季賽 | 第二題設計及解題思路
緊隨其後,再次轉換:
2020 KCTF秋季賽 | 第二題設計及解題思路

上圖程式碼將ebp-36c中資料重組,放到ebp-dc緩衝區,處理後結果如下圖:
2020 KCTF秋季賽 | 第二題設計及解題思路



禮貌的開場白

第一個演算法5614C0-5615BC,注意規避561523-561553之間的反除錯程式碼:

2020 KCTF秋季賽 | 第二題設計及解題思路

2020 KCTF秋季賽 | 第二題設計及解題思路


據索引算出hash值,hash演算法如下: 
unsigned short calc_hash(short val){    long _seed = val >= 0 ? val : 0x10000 + val;    for (size_t i = 0; i < 101; i++)    {        _seed *= 0x343FD;        _seed += 0x269EC3;    }    _seed >>= 0x10;    _seed &= 0x7fff;    return (unsigned short)_seed;}

等價邏輯:
short m = 0,n = 0for (m = 0, m < length:word:[ebp-dc], ++m){    if (word:[ebp-dc][m] == calc_hash(m))    {        dword:[ebp-4fc] = n    }    else if(word:[ebp-dc][m] == calc_hash(-m))    {        ++n    }    else    {        print "一個禮貌的開場白"        exit    }}if(n < 12){    print "行星太少了"    exit}

根據分析內容,做個禮貌的回應,構造一個最短長度的測試註冊碼:675EE1125B47D47B4E30C8644219BC4D3602B036296BA31F,再次執行,'開場白'已過,是時候進入真正的驗證了。


有人說這題是無解的

分析隨後的程式碼:
2020 KCTF秋季賽 | 第二題設計及解題思路
此時,ebp-4fc為上文中解析出的索引值集合,如下:
2020 KCTF秋季賽 | 第二題設計及解題思路

驗證邏輯:

fill [ebp-78], 0, 100short i = 0, j = 0;for (i = 0; i < length:dword:[ebp-4fc] - 1; ++i){     for (j = i + 1; j < length:dword:[ebp-4fc]; ++j)     {         dis = dword:[ebp-4fc][j] - dword:[ebp-4fc][i]         if(byte:[ebp-78][dis] == 0)         {              byte:[ebp-78][dis] = 1         }         else         {             print "此題無解"             exit         }           }}

那麼根據上面的驗證邏輯,什麼樣的註冊碼是滿足條件的呢?

考慮這樣一個數列:一個最少包含12個數的自然數數列,其中任意兩數的差都不相等。
如果這樣的數列存在,那麼題中的驗證邏輯是可以滿足的,那麼是否存在這樣的數列呢?百度一下。

2020 KCTF秋季賽 | 第二題設計及解題思路已經找到了我們想要的答案,那就是: 0,2,6,24,29,40,43,55,68,75,76,85上程式碼:

2020 KCTF秋季賽 | 第二題設計及解題思路



驗證

2020 KCTF秋季賽 | 第二題設計及解題思路

完工。


2020 KCTF秋季賽 | 第二題設計及解題思路

賽場風雲變幻,不到最後輸贏難見分曉。

現在第四題已經開放,快來繼續挑戰自己吧!


2020 KCTF秋季賽 | 第二題設計及解題思路


越早提交答案,得分越高哦!
立即掃碼逆風翻盤!

2020 KCTF秋季賽 | 第二題設計及解題思路

華為全面屏智慧電視、Xbox One X、JBL 無線藍芽耳機等你來拿哦!


相關文章