目錄導航
- 打開題目審題
- 找到突破口
- 源代碼分析
- 解題步驟
- 相關知識
打開題目審題

Daddy told me about cool MD5 hash collision today.
I wanna do something like that too!
ssh col@pwnable.kr -p2222 (pw:guest)
題目中提到MD5哈希碰撞,可以基本確定方向
輸入命令回車,輸入密碼:guest 即可登錄服務器,

找到突破口
照例先
ls -la
col@pwnable:~$ ls -la
total 36
drwxr-x--- 5 root col 4096 Oct 23 2016 .
drwxr-xr-x 115 root root 4096 Dec 22 2020 ..
d--------- 2 root root 4096 Jun 12 2014 .bash_history
-r-sr-x--- 1 col_pwn col 7341 Jun 11 2014 col
-rw-r--r-- 1 root root 555 Jun 12 2014 col.c
-r--r----- 1 col_pwn col_pwn 52 Jun 11 2014 flag
dr-xr-xr-x 2 root root 4096 Aug 20 2014 .irssi
drwxr-xr-x 2 root root 4096 Oct 23 2016 .pwntools-cache
發現flag檔案,嘗試查看(肯定看不了
fd@pwnable:~$ cat flag
cat: flag: Permission denied
fd@pwnable:~$
果然不能看,看下別的,檔案flag肯定是最后的答案,所以需要看看其他檔案找尋突破口,
還有一個c語言源檔案col.c和可執行檔案col,
那沒什么說的,看看col.c,
col@pwnable:~$ cat col.c
#include <stdio.h>
#include <string.h>
unsigned long hashcode = 0x21DD09EC;
unsigned long check_password(const char* p){
int* ip = (int*)p;
int i;
int res=0;
for(i=0; i<5; i++){
res += ip[i];
}
return res;
}
int main(int argc, char* argv[]){
if(argc<2){
printf("usage : %s [passcode]\n", argv[0]);
return 0;
}
if(strlen(argv[1]) != 20){
printf("passcode length should be 20 bytes\n");
return 0;
}
if(hashcode == check_password( argv[1] )){
system("/bin/cat flag");
return 0;
}
else
printf("wrong passcode.\n");
return 0;
}
找到重點 system("/bin/cat flag");
看到原始碼大概已經明白了,我們沒有查看檔案的權限,但是fd有,所以只要分析如何讓代碼執行system("/bin/cat flag");即可,
源代碼分析
col@pwnable:~$ cat col.c
#include <stdio.h>
#include <string.h>
unsigned long hashcode = 0x21DD09EC;
unsigned long check_password(const char* p){
int* ip = (int*)p;
int i;
int res=0;
for(i=0; i<5; i++){
res += ip[i];
}
return res;
}
int main(int argc, char* argv[]){
// 要有引數
if(argc<2){
printf("usage : %s [passcode]\n", argv[0]);
return 0;
}
// 第一個引數長度必須是20位元組
if(strlen(argv[1]) != 20){
printf("passcode length should be 20 bytes\n");
return 0;
}
// 對argv[1]進行check_password 結果與hashcode 相等
if(hashcode == check_password( argv[1] )){
system("/bin/cat flag");
return 0;
}
else
printf("wrong passcode.\n");
return 0;
}
單獨分析
unsigned long check_password(const char* p)
unsigned long check_password(const char* p){
int* ip = (int*)p;
int i;
int res=0;
for(i=0; i<5; i++){
res += ip[i];
}
return res;
}
res 就是將 引數每四個位元組相加后的結果,聯系main函式,可知我們輸入引數每四個位元組相加必須hashcode = 0x21DD09EC
解題步驟
其實就是計算五個數,五個數相加等于 0x21DD09EC 算一下這五個數, 設為 a、a、a、a、a + b

繼續計算器計算:0x06C5CEC8 * 5 = 0x21DD09E8 與原數相差 4
五個數就計算出來了
0x06C5CEC8、0x06C5CEC8、0x06C5CEC8、0x06C5CEC8、0x06C5CECC
但是我們如何保證我們輸入的數正好以我們想要的形式存在呢,直接作為引數輸入的話,
col@pwnable:~$ ./col 06C5CEC806C5CEC806C5CEC806C5CEC806C5CECC
passcode length should be 20 bytes
col@pwnable:~$
明顯是不對的,會把每個字符讀作一個位元組,這樣就有40位元組了,
我們實際要將這些值直接寫到記憶體里,也即這些值是已經對應好的ascii值,并且是大端模式,對應資料的高位元組存放在低地址,【可以使用GDB進行除錯或者兩種模式都輸入嘗試】print 從高地址向低地址寫,因此綜合與我們書寫相反,【大小端應該都懂】
那么問題來了,如何將這些值直接寫到記憶體里呢? 使用命令:
"print '\xCC\xCE\xC5\x06'+'\xC8\xCE\xC5\x06'*4"
構造輸入命令:
./col $(python -c "print '\xCC\xCE\xC5\x06'+'\xC8\xCE\xC5\x06'*4")
python -c是指用python解釋器執行print ‘\xCC\xCE\xC5\x06’+’\xC8\xCE\xC5\x06’*4
$()指令是括號內的字串被shell解釋為命令列,在執行時,shell首先執行該命令列,并以它的標準輸出結果取代整個$(),整個陳述句的意思是將引數
‘\xCC\xCE\xC5\x06\xC8\xCE\xC5\x06\xC8\xCE\xC5\x06\xC8\xCE\xC5\x06\xC8\xCE\xC5\x06’
直接寫入到記憶體,
col@pwnable:~$ ./col $(python -c "print '\xCC\xCE\xC5\x06'+'\xC8\xCE\xC5\x06'*4")
daddy! I just managed to create a hash collision :)
daddy! I just managed to create a hash collision 😃
驗證即可

相關知識
介紹與上一篇有重復
MD5哈希碰撞
md5是一種被廣泛使用的密碼散列函式,可以產生一個128位的(16進制)散列值,2004年,我國中科院院士王小云證實md5演算法無法防止碰撞,因此,不適用于安全性認證,
MD5作為檔案校驗方法已經不可靠了,可以人為制造碰撞,
int main( int argc , char * argv[ ] ,char * envp[ ])
main函式的引數串列保存了輸入引數的資訊:
第一個引數argc記錄了輸入引數的個數,且argc是包括程式本身在內的引數個數,如本題目,直接./fd運行,該引數實際上是1,而不是0,
第二個引數是字串陣列的,字串陣列的每個單元是char*型別的,指向一個c風格字串,arg[ ]指向的陣列中至少有一個字符指標,即arg[0].他通常指向程式中的可執行檔案的檔案名,
第三個引數是用來取得系統的環境變數,如:在DOS下,有一個PATH變數,當你在DOS提示符下輸入一個命令的時候,DOS會首先在當前目錄下找這個命令的執行檔案,如果找不到,則到PATH定義的路徑下去找,找到則執行,找不到回傳Bad command or file name ,在DOS命令提示符下鍵入set可查看系統的環境變數,
$(python -c “print ‘\xCC\xCE\xC5\x06’+’\xC8\xCE\xC5\x06’ * 4”)詳解
$(linux命令) 在執行時,shell首先執行該命令列,并以它的標準輸出結果取代整個$()
python -c 可以在命令列中呼叫 python 代碼, 實際上 -c 就是 command 的意思
print str 將字串寫入到記憶體中
參考:
https://blog.csdn.net/jxz_dz/article/details/102755923
https://zhuanlan.zhihu.com/p/131283811
https://www.pythonheidong.com/blog/article/147292/6142802b1e89b9028188/
http://blog.chinaunix.net/uid-28463456-id-4090821.html
https://www.cnblogs.com/chnmig/p/14207100.html
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/352123.html
標籤:其他
