前天花了一個下午的時間刷了幾道IDF實驗室的題目, 這個網站實在是有點冷清, 題目也比較少, 所以就被我和師兄們刷榜了2333...
因為我最先開始做, 所以就干脆刷到第一去了.
題目很水, 切莫見怪.
牛刀小試
http://ctf.idf.cn/index.php?g=game&m=list&a=index&id=16
莫爾斯密碼: 網上有轉換器, 轉換后去空格全小寫就是flag. flag: wctf{morseode}
ASCII碼而已: 這是Unicode碼好吧...隨便找個語言Print一下即可, 我用的是java, 為什么那么執着於粉絲呢? flag: wctf{moremore_weibo_fans}
最簡單的題目: F12, 在源碼里面搜索, flag: wctf{woldy_s_weibo}
啥: 把圖片用WinHex打開, 找flag: wctf{mianwubiaoqing__}
被改錯的密碼: 注意到給出的密文里面出現了一個不合時宜的字母l, 去掉后在cmd5查詢得到flag: wctf{idf}
-天孤劍-的微博: 在首頁「寒 近 嗜 好 酒 劍 動 秀 清 風」里面的「劍」就能找到flag: wctf{@無所不能的魂大人} (廣告真是無處不在
包羅萬象
http://ctf.idf.cn/index.php?g=game&m=list&a=index&id=17
圖片里的英語: 好吧這題是我腦洞不夠大, 是同學告訴我做法的, 圖片改名成任意壓縮文件, 解壓得到趙本山的劇照, flag就是劇照里面的台詞首字母, 圖片本來是沒有字幕, 百度之
台詞是「May the force be with you」, flag: wctf{Mtfbwy}
逆天而行
http://ctf.idf.cn/index.php?g=game&m=list&a=index&id=21
逆向題起了這么個名字…
只做了三題,
Python BtyeCode: 好像是用Cpython編譯的, 手頭沒工具, 用的是師兄給的反編譯出來的代碼:
1 def encrypt(key, seed, string): 2 rst = [] 3 for v in string: 4 rst.append((ord(v) + seed ^ ord(key[seed])) % 255) 5 seed = (seed + 1) % len(key) 6 return rst 7 if __name__ == '__main__': 8 print "Welcome to idf's python crackme" 9 flag = input('Enter the Flag: ') 10 KEY1 = 'Maybe you are good at decryptint Byte Code, have a try!' 11 KEY2 = [ 12 124, 48, 52, 59, 164, 50, 37, 62, 67, 52, 48, 6, 1, 13 122, 3, 22, 72, 1, 1, 14, 46, 27, 232] 14 en_out = encrypt(KEY1, 5, flag) 15 if KEY2 == en_out: 16 print 'You Win' 17 else: 18 print 'Try Again !'
正常人反着推肯定是寫: arr[i] - (key[seed] ^ seed);
不過這樣肯定推不出來, 因為出題人寫錯了… (師兄想出來的… 打死我也不知道是題目錯了啊…), 出題的用了正確的算法給出密文之后, 把
rst.append((ord(v) ^ ord(key[seed]) + seed) % 255) 寫成了:
rst.append((ord(v) + seed ^ ord(key[seed])) % 255)
所以沒必要深究什么了:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <math.h> 5 int main(){ 6 unsigned char arr[] = { 7 124, 48, 52, 59, 164, 50, 37, 8 62, 67, 52, 48, 6, 1, 122, 3, 9 22, 72, 1, 1, 14, 46, 27, 232, 0}; 10 const char key[] = "Maybe you are good at decryptint Byte Code, have a try!"; 11 char flag[100]; 12 int tmp; 13 int seed = 5; 14 for (int i = 0; i < 23; i++){ 15 tmp = (arr[i] ^ key[seed]) - seed; 16 flag[i] =abs(tmp)%127; //事實上為什么要%127也不太清楚 17 while (flag[i] < 32 || flag[i] > 127){ 18 if (flag[i] < 32) flag[i] = flag[i] + 255; 19 if (flag[i] > 127) flag[i] = flag[i] - 255; 20 } 21 printf("arr[%d] = %d; key[%d] = %d; ^ = %d flag[%d] = %d;\n", 22 i,arr[i],seed, key[seed],key[seed]^seed, i, flag[i]); 23 seed = (seed + 1)%strlen(key); 24 } 25 puts(flag); 26 printf("\n"); 27 system("pause"); 28 return 0; 29 }
flag: WCTF{ILOVEPYTHONSOMUCH}
簡單的ELF逆向:
這題是ELFx64位的CrackMe, 只能用IDA啦, 載入之,師兄叫我用F4 F5,不過64位的IDA好像沒有F5, 找到main函數, F4, 得到代碼:
1 addr_0x400900_12: 2 { 3 v13 = 0; 4 if (v3 != 22) { 5 v13 = 1; 6 } 7 v14 = 0; 8 while ((unsigned char)(uint1_t)(v14 <= 16) != 0) { 9 eax15 = (int32_t)(uint32_t)(unsigned char)v8; 10 if ((int32_t)*(signed char*)&eax15 != 0) { 11 v13 = 1; 12 *(int32_t*)&rsi = 0; 13 *((int32_t*)&rsi + 1) = 0; 14 printf("%d", 0); 15 } 16 ++v14; 17 } 18 eax16 = (int32_t)(uint32_t)(unsigned char)v17; 19 if (*(signed char*)&eax16 != 48 20 || ((eax18 = (int32_t)(uint32_t)(unsigned char)v19, *(signed char*)&eax18 != 56) 21 || ((eax20 = (int32_t)(uint32_t)(unsigned char)v21, *(signed char*)&eax20 != 50) 22 || ((eax22 = (int32_t)(uint32_t)(unsigned char)v23, *(signed char*)&eax22 != 51) 23 || (eax24 = (int32_t)(uint32_t)(unsigned char)v25, *(signed char*)&eax24 != 0x7d))))) { 24 v13 = 1; 25 } 26 27 puts("\r", rsi); 28 /* v13 應該是一個標志變量 */ 29 if (v13 != 0) { 30 puts("u r wrong\r\n\r", rsi); 31 rax26 = main("u r wrong\r\n\r", rsi); 32 } else { 33 puts("u r right!\r", rsi); 34 } 35 return 0; 36 addr_0x4008ff_7: 37 goto addr_0x400900_12; 38 }
果然代碼的可讀性不是很好, 前面的printf之類的被我省去了, 重點放在while循環和那個if上, 可以看到if要求的是幾個變量必須分別為 0, 8, 2, 3,} 應該就是flag 的后部分了, 從最后的判斷right和wrong可以看出v13是判斷正確與否的變量.
while循環是在是難懂, 乖乖回去看匯編好了.
右鍵選擇Graphic View模式, 這樣匯編代碼顯得很清晰, 把重點放在while循環對應的那部分, 簡單分析得到, 紅筆標注的地方就是程序內為數不多的循環了, 循環之后多條並排的綠線那里是多路if,最后的是正確與否的判斷以及輸出.
關鍵代碼如下, interator 對應var_14, arr_1 對應var_40, arr_2 對應 var_c0:
1 loc_40097C: 2 cmp [rbp+iterator], 10h ; 循環總次數 3 setle al 4 test al, al 5 jnz short loc_40091D 6 7 loc_40091D: 8 mov eax, [rbp+iterator] ; 裝入循環變量 9 cdqe 10 movzx eax, [rbp+rax+arr_1] 11 movsx edx, al ; 取出(unsigned char)arr_1[iterator], 數組元素只有一個字節 12 mov eax, [rbp+iterator] 13 cdqe 14 mov eax, [rbp+rax*4+arr_2] ; 取出(int)arr_2[iterator], 四個字節 15 sub eax, 1 ; eax = eax - 1 16 mov ecx, eax ; ecx = eax 17 shr ecx, 1Fh ; ecx = ecx >> 0x1f 18 lea eax, [rcx+rax] ; 裝入地址其實就是 eax = ecx + eax; 19 sar eax, 1 ; eax = eax >> 1 20 cmp edx, eax ; 比較arr_2[iterator]經過運算的值是否等於arr_1[iterator] 21 jz short loc_400978 ; 等於則跳 22 23 loc_400978: 24 add [rbp+iterator], 1
經過以上分析可以知道 arr_1 應該是我們輸入的key, 所以有必要知道arr_2 的值,
跳轉到arr_2的定義:
是空的…
但是我們回到代碼中, 對arr_2有這樣的操作:
剛好17個項(0-10h),
所以說 arr_i[i] = ((arr_2[i] – 1) + (arr_2[i] – 1)>>0x1f)>>1
(忽略了shr 和 sar 以及各種細節問題… 所幸沒有出錯)
(vim 來處理這些最爽了)
代碼:
#include <cstdio> #include <cstdlib> #define N 17 int arr_2[N] = { 0x0EF, 0x0C7, 0x0E9, 0x0CD, 0x0F7, 0x8B, 0x0D9, 0x8D, 0x0BF, 0x0D9, 0x0DD, 0x0B1, 0x0BF, 0x87, 0x0D7, 0x0DB, 0x0BF }; int main(){ for (int i = 0; i < N; i++){ int ch = ((arr_2[i] - 1) + ((arr_2[i] - 1) >> 0x1f))>>1; /*注意一下 >> 的優先級*/ printf("%c",ch); } printf("0823}\n"); system("pause."); return 0; }
flag: wctf{ElF_lnX_Ckm_0823}
簡單的PE文件逆向:
x86平台, 雙擊沒法運行, 應該需要某個古老的C++運行時, 那就放棄用OD了, IDA載入, 稍微翻一翻(其實是不知道如何有效定位), 0x4113a0處就是關鍵處, F5之, 這次代碼好看多了, 可以看出和上一個CrackMe基本相同…
1 flag = 0; 2 for ( i = 0; i < 17; ++i ){ 3 if ( v76[i] != byte_415768[*(&v53 + i)] ) 4 flag = 1; 5 } 6 if ( v77 != 49 || v78 != 48 || v79 != 50 || v80 != 52 || v81 != 125 ) 7 flag = 1; 8 v76[v75] = 0; 9 printf("\r\n"); 10 sub_411136(); 11 if ( flag ) 12 { 13 printf("u r wrong\r\n\r\n"); 14 sub_411136(); 15 sub_41113B(); 16 } 17 else 18 { 19 printf("u r right!\r\n"); 20 sub_411136(); 21 } 22 system("pause");
同樣是把flag分成兩部分, 后面五個必須是1024},前面的在一個for循環里算出:
v76[i] != byte_415768[*(&v53 + i)]
通過一個數組v53[]運算出下標, 再用下標從另一個數組byte_415768[]取出值來, 數組是:
1 v53 = 1; 2 v54 = 4; 3 v55 = 14; 4 v56 = 10; 5 v57 = 5; 6 v58 = 36; 7 v59 = 23; 8 v60 = 42; 9 v61 = 13; 10 v62 = 19; 11 v63 = 28; 12 v64 = 13; 13 v65 = 27; 14 v66 = 39; 15 v67 = 48; 16 v68 = 41; 17 v69 = 42; 18 byte_415768 db 73h 19 db 'wfxc{gdv}fwfctslydRddoepsckaNDMSRITPNsmr1_=2cdsef66246087138',0
要注意byte_415768[]的一個元素s(73h)沒有被識別.
所以:
1 #include <cstdio> 2 #include <cstdlib> 3 int v53[] = { 4 1, 4, 14, 10, 5, 36, 23, 42, 13, 5 19, 28, 13, 27, 39, 48, 41, 4 6 }; 7 char byte_415768[] = "swfxc{gdv}fwfctslydRddoepsckaNDMSRITPNsmr1_=2cdsef66246087138\0"; 8 9 int main(){ 10 for (int i = 0; i < 17; i++){ 11 printf("%c", byte_415768[v53[i]]); 12 } 13 printf("1024}\n"); 14 system("pause"); 15 }
flag: wctf{Pe_cRackme1c1024}
凱撒加密:
給出一個字符串, 寫個程序按127為周期跑一圈, 得到二十多個處在合法范圍內的串, 最正常的一個如下, 像是base64, 解碼一下得出flag.
dGhlIGZsYWcgaXMgd2N0ZntrYWlzYV9qaWFhYWFhbWl9LHBseiBmbG93IG15IHdlaWJvLGh0dHA6Ly93ZWliby5jb20vd29sZHk=
代碼:
1 #include <cstdio> 2 #include <cstdlib> 3 #include <cstring> 4 char kaiser[] = "U8Y]:8KdJHTXRI>XU#?!K_ecJH]kJG*bRH7YJH7YSH]*=93dVZ3^S8*$:8\"&:9U]RH;g=8Y!U92'=j*$KH]ZSj&[S#!gU#*dK9\\."; 5 /* 記得轉義 */ 6 int main(){ 7 char ch[1000]; 8 bool flag = true; 9 for (int i = 0; i < 127; i++){ 10 memset(ch, 0, sizeof(ch)); 11 for (int j = 0; j < strlen(kaiser); j++){ 12 ch[j] = (kaiser[j] + i)%127; 13 if (ch[j] <= 32 || ch[j] > 127) flag = false; 14 } 15 if (flag) puts(ch); 16 } 17 system("pause"); 18 return 0; 19 }
flag: wctf{kaisa_jiaaaaami}
天羅地網
http://ctf.idf.cn/index.php?g=game&m=list&a=index&id=22
一種編碼而已: Jother編碼, JSのBrainFuck, 前幾天剛在Wooyun看到…直接在F12控制台執行得到:
flag: WCTF{H3110_J0t4er}
超簡單的JS題: 查看源碼發現script標簽里有這一段:
1 var p1 ='%66%75%6e%63%74%69%6f%6e%20%63%68%65%63%6b%53%75%62%6d%69%74%28%29%7b%76%61%72%20%61%3d%64%6f%63%75%6d%65%6e%74%2e%67%65%74%45%6c%65%6d%65%6e%74%42%79%49%64%28%22%70%61%73%73%77%6f%72%64%22%29%3b%69%66%28%22%75%6e%64%65%66%69%6e%65%64%22%21%3d%74%79%70%65%6f%66%20%61%29%7b%69%66%28%22%65%66%66%35%62%66%38%63%63'; 2 var p2 ='%31%61%30%64%38%33%35%36%32%63%35%22%3d%3d%61%2e%76%61%6c%75%65%29%72%65%74%75%72%6e%21%30%3b%61%6c%65%72%74%28%22%45%72%72%6f%72%22%29%3b%61%2e%66%6f%63%75%73%28%29%3b%72%65%74%75%72%6e%21%31%7d%7d%64%6f%63%75%6d%65%6e%74%2e%67%65%74%45%6c%65%6d%65%6e%74%42%79%49%64%28%22%6c%65%76%65%6c%51%75%65%73%74%22%29%2e%6f%6e%73%75%62%6d%69%74%3d%63%68%65%63%6b%53%75%62%6d%69%74%3b'; 3 eval(unescape(p1) + unescape('%33%66%61%37%38%32%66%32%36%31%61%35' + p2));
JS我是完全不懂…
查了一下unescape()發現這個函數是用來解碼字符串的, 大概是為了讓URL參數支持Unicode吧… eval()用來執行參數指定的JS代碼, 有點MetaPrograming(元編程)的意思.
找個在線解碼: http://tool.chinaz.com/Tools/Escape.aspx
1 var p1 = 'function checkSubmit(){var a=document.getElementById("password");if("undefined"!=typeof a){if("eff5bf8cc'; 2 var p2 ='1a0d83562c5"==a.value)return!0;alert("Error");a.focus();return!1}}document.getElementById("levelQuest").onsubmit=checkSubmit;'; 3 eval(unescape(p1) + unescape('3fa782f261a5' + p2));
合並起來就是:
1 function checkSubmit(){ 2 var a=document.getElementById("password"); 3 if("undefined"!=typeof a){ 4 if("eff5bf8cc3fa782f261a51a0d83562c5"==a.value) 5 return!0; 6 alert("Error"); 7 a.focus(); 8 return!1 9 } 10 } 11 document.getElementById("levelQuest").onsubmit=checkSubmit;
這下就清晰了, 輸入eff5bf8cc3fa782f261a51a0d83562c5提交:
flag: wctf{webclieNt_c0py}
古老的郵件編碼: 不太清楚郵件編碼什么的, 稍微搜索后發現它比較符合 Uuencode編碼的特征, 找在線轉換工具: http://web.chacuo.net/charsetuuencode
flag: wctf{uuuuuencode__}
剩下的題就沒做了(其實是不會做).