比賽打到中午就溜溜球去新生賽幫忙了,所以試圖復盤此次比賽部分題目
因個人水平有限,復現的題目僅包括(每題提供附件):
- misc全部題目(重生之我在A國當間諜,pikapikapika,雙擊開始冒險)
- re部分題目(Wordy,easy_low)
逆向剩余的部分題目有個師傅在52上寫了,我舔!https://www.52pojie.cn/thread-1551240-1-1.html
Misc
重生之我在A國當間諜
鏈接:https://pan.baidu.com/s/1pkAtfcm5MI4hSBFFu-8yHQ
提取碼:eqyq
拿到題目后 winhex 打開
看到 ASCII 部分很像 hex 值,於是復制出來再以 hex 的形式保存到新的文件
看到有串長得很像 base64 的字符串
base64decode 一下,得到解壓密碼:[1e4rl0/e
但是賽后看大佬 wp 得知這其實是 PDU 編碼,一句一句解密就可以,網址:http://www.sendsms.cn/pdu/
倒數第二句解密出來是:真讓人無語。我把密碼告訴你,記住,這個密碼不要告訴任何人。WzFlNHJsMFwvZQ==
算是非預期了吧,這題的分我本不配拿QAQ
言歸正傳,繼續往下做題,用密碼解開壓縮包以后,看到是一張撕毀了的二維碼,本來想着截圖成一塊一塊拼到一起,結果顯然是不行的,啥也掃不出來。於是扔給會 ps 的咸魚舍友,才終於拼出了flag
GFCTF{ctf_Ha0_NaN_aaAaaaAaaaaAaaaaaAaa}
pikapikapika
鏈接:https://pan.baidu.com/s/1gcQxhrQvPRFMhQhEBTiPLA
提取碼:lze7
拿到題目 binwalk 發現有個壓縮包
分離出來后解壓提示需要密碼,看一眼皮卡丘
按照這個順序組合出解壓密碼為:I_want_a_p1ka!
解壓后得到 flag.wav,用 Audacity 打開看看,發現一共 2 種頻率,不難想到低位對應 0,高位對應 1
不會寫腳本,抱着僥幸心理想會不會有用信息就一部分,往后就都是重復的,手擼了一會后發現其實並不,於是就心安理得擺爛了(叉腰
賽后看了大佬的 wp,才得以了解此題
有一點注意的是跑腳本前需要處理一下 wav 文件,只保留其中的 data 部分,如下圖所示
腳本如下(來源:套神)
f = open('flag.wav','rb').read()
flag = ''
for i in range(len(f)//2):
if(f[i*2:i*2+2] == b'\x98:'):
flag += '0'
else:
flag += '1'
s = ''
rflag = ''
for i in flag:
s+=i
if len(s)==8:
rflag += chr(int(s,2))
s=''
print(rflag)
得到一串 base64,放到 cyberchef 解密一下保存為 png,發現很明顯改了圖片的高,改高一點就可以發現 flag 啦
GFCTF{fe1a-17f7-a7f6-1f8f534e-ef3974-c049c5}
雙擊開始冒險
鏈接:https://pan.baidu.com/s/1Q4GrX8XGgVeQkt0JugJcSw
提取碼:cgai
第一步爆破密碼,四位數,上 AZPR
解壓后來到第二層,打開 hint 拉到最右邊,中間的地方有句話
搜索對應 QQ 號,看到簽名(因為復現時時間已經是賽后兩天了,所以此人只保留了簽名,比賽時名字也是base64,雖然我強調這個好像也沒什么必要),WW91IGxvdmUgbWUsIEkgbG92ZSB5b3U=
base64decode 得到:You love me, I love you,解壓壓縮包,來到第三層
看到給出了一個 usb 鼠標流量,此前只接觸過鍵盤流量,上網搜了幾篇文章對着弄但不知道為啥啥也提取不出來,於是又順理成章擺爛了
賽后看別人 wp,發現大家都能提取出來,整個被大疑惑到,但我肯定我確實是提取不出來,又通過一中午的不懈嘗試,終於發現雖然在 kali 里提取了個寂寞但是在 windows 下卻可以提取出來......原因不明,但覺得自己血虧
tshark -r usb.pcapng -T fields -e usb.capdata > usbdata.txt
提取后發現有很多空行,想着用 sed 一步到位然而發現又什么都提取不出來,只得手動腳本加工來把空行去掉
file1 = open('usbdata.txt', 'r', encoding='utf-8')
file2 = open('usbdat.txt', 'w', encoding='utf-8')
for line in file1.readlines():
if line == '\n':
line = line.strip("\n")
file2.write(line)
file1.close()
file2.close()
得到了一串 16 位的流量數據,提取第 0 2 4 6 字節來畫圖(說明:接下來的腳本來源均為套神博客)
f = open('usbdat.txt','r').readlines()
f1 = open('usb.txt','w')
for i in range(len(f)):
f[i] = f[i].split(':')
for i in range(len(f)):
tmp = ''
for j in range(4):
tmp += f[i][j*2]+':'
f1.write(tmp)
f1.write('\n')
接下來轉換成坐標(y坐標需要加個負號)
nums = []
keys = open('usb.txt','r')
result=open('result.txt','w')
posx = 0
posy = 0
for line in keys:
if len(line) != 13:
continue
x = int(line[3:5],16)
y = int(line[6:8],16)
if x > 127 :
x -= 256
if y >127 :
y -=256
posx += x
posy += y
btn_flag = int(line[0:2],16) # 1 for left , 2 for right , 0 for nothing
if btn_flag == 1:
result.write(str(posx)+' '+str(-posy)+'\n')
keys.close()
result.close()
用 gnuplot 畫圖得到:
7724774CTF,解壓得到
winhex 打開,flag 就在結尾
GFCTF{this_is_rea1_fllllll11ag}
RE
Wordy
鏈接:https://pan.baidu.com/s/1B7HFDUk1PC_d3WTYW9X6mw
提取碼:mt1t
IDA 打開,看到 hex 窗口,很明顯可以看到一串可疑字符
讀一下,hello world! There are moments in...
bb了賊長的一段,往下拉一段距離就可以看到 flag
GFCTF{u_are2wordy}
easy_low
鏈接:https://pan.baidu.com/s/1bNQ1MDZZ7ucqhPR1x0rCYg
提取碼:p4w8
jadx 打開,定位到 MainActivity
簡單分析一下,看到點擊事件 onClick,先看第一部分
這一部分輸入兩個字符串,用戶名和密碼,並且可以得知二者長度均為 16,然后進入下一部分
用戶名先通過 encode 函數進行加密,查看一下 encode
函數傳入輸入的用戶名也就是 str,以及一個已知數組,通過 for 循環進行兩次變換,最后變換后的結果與其原值一致,所以可以據此寫出腳本爆破出用戶名
#include <bits/stdc++.h>
using namespace std;
int num_list[] = {23, 22, 26, 26, 25, 25, 25, 26, 27, 28, 30, 30, 29, 30, 32, 32};
string user_name;
inline bool check(int a, int pos) {
int num = ((a + num_list[pos]) % 61) * 2 - pos;
if (num == a) return true;
else return false;
}
int main() {
for (int i = 0; i < 16; i++)
for (int j = 0; j < 125; j++)
if (check(j, i)) user_name += (char)j;
cout << user_name << endl;
return 0;
}
得到用戶名為:LOHILMNMLKHILKHI
然后繼續看獲取密碼的部分
對用戶名每一位異或 34,取前 10 位存入 str,str 再與 '_' 以及 bArr 拼接,然后將 str 與用戶輸入的密碼作比較,因此可以逆向出密碼
#include <bits/stdc++.h>
using namespace std;
int Llist[] = {64, 48, 48, 49, 49};
int num_list[] = {23, 22, 26, 26, 25, 25, 25, 26, 27, 28, 30, 30, 29, 30, 32, 32};
int num[] = {0xF2, 0xE, 0x95, 0x5D, 0xDE, 0xA2, 0xCC, 0x86, 0xF4, 0xBB, 0x39, 0xC0, 2, 0x81, 0x94, 0xC5, 0x9C, 0x5E, 0xD5, 0xAE, 0x77, 0x43, 0xF1, 0x89};
string user_name, user_pass;
inline bool check(int a, int pos) {
int num = ((a + num_list[pos]) % 61) * 2 - pos;
if (num == a) return true;
else return false;
}
int main() {
for (int i = 0; i < 16; i++)
for (int j = 0; j < 125; j++)
if (check(j, i)) user_name += (char)j;
for (int i = 0; i < 16; i++)
user_name[i] = (char)((int)user_name[i] ^ 34);
for (int i = 0; i < 10; i++) user_pass += user_name[i];
user_pass += '_';
for (int i = 0; i < 5; i++) user_pass += Llist[i];
cout << user_pass;
return 0;
}
得到密碼為:nmjknoloni_@0011
接下來可以發現該活動通過 Intent 將密碼傳進了 o0 這個活動里面,o0 如下
o0 類把密碼又傳給了 b 類,查看 b
b 類調用了一個 f 類的 oho 方法,查看 f
oho 在 native 層,總之,它就是最終的加密⽅法。它將我們的密碼,用戶名以及在第三個界⾯輸入的內容進行了加密並做比較,返回值 boolean 正確則輸出 Congratulations...
反編譯 apk,把 so 文件拖入 IDA,找到 oho
這個函數先對傳入的參數的長度進行比較,得知 flag 的長度應當為 24
然后看循環部分,通過分析可以發現是將密碼每次 2 位傳給 src,用戶名則是先倒置再每次 2 位接在 src 后面,然后再與用戶第三個輸入的內容拼接后傳入 encode 進行最終的加密,查看 encode
看不懂嗚嗚嗚,菜雞落淚,找了出題師傅要的腳本,才勉強看懂了
#include <bits/stdc++.h>
using namespace std;
int main() {
char key1[]={"nNjLnHlL"};
int count=1;
int key[7];//nNjLnHlL
for (int i = 0; i < 6; ++i) {
key[i] = key1[count];
count++;
}
unsigned int ks[6]={0x5d950ef2,0x86cca2de,0xc039bbf4,0xc5948102,0xaed55e9c,0x89f14377};
unsigned int k=0,bk=0;
unsigned int p[4];
for(int i=5;i>=0;i--)
if(i>0) ks[i]^=ks[i-1];
for(int i=0;i<24;i+=4){
k=ks[i/4];
k=(1<<key[i/4])^k;
k=((k>>16)) | ((~(k<<16))&0xffff0000); //這一句還有些疑問,求會的師傅講講QAQ,小的定感激不盡
k=((k<<key[i/4])) | (k>>(32-key[i/4]));
for(int j=0; j<4; j++) printf("%c", *((char*)&k+3-j));
}
return 0;
}
解出:WelCOme_To_mAkaBakA!BrO!,包裹上 GFCTF{} 得到 flag
GFCTF{WelCOme_To_mAkaBakA!BrO!}
此次比賽感悟:大抵是因為題目相對簡單的緣故,最起碼在做題的時候能略微有些思路了,然而也暴露出了許多問題,例如腳本屁也不會寫。總而言之,這次比賽是一個令我這種啥也不會的萌新參與感很高的一次比賽,雖然做出來的題不多,但每個題都能有些思路並且做幾步。雖說給自己的方向的定位是逆向,但是在比賽中還是雜項做的更多一些,說到底還是人太菜了,尤其是安卓逆向最后那個 native 層的加密,不看師傅的解題腳本根本就不知道在說什么,從今往后還是要多刷題,還差得遠啊qwq