題目一(fd - 1 pt )
打開題目,使用SSH鏈接到目標服務器。發(fā)現(xiàn)有三個文件分別為fd、fd.c 、flag。
fd@ubuntu:~$ ls
fd fd.c flag
fd@ubuntu:~$ cat flag
cat: flag: Permission denied
fd@ubuntu:~$ cat fd.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char buf[32];
int main(int argc, char* argv[], char* envp[]){
if(argc<2){
printf("pass argv[1] a number\n");
return 0;
}
int fd = atoi( argv[1] ) - 0x1234;
int len = 0;
len = read(fd, buf, 32);
if(!strcmp("LETMEWIN\n", buf)){
printf("good job :)\n");
system("/bin/cat flag");
exit(0);
}
printf("learn about Linux file IO\n");
return 0;
}
fd@ubuntu:~$ ./fd
pass argv[1] a number
fd@ubuntu:~$ ./fd 3
learn about Linux file IO
fd@ubuntu:~$ ./fd 2
learn about Linux file IO
fd@ubuntu:~$ ./fd 1
learn about Linux file IO
嘗試輸入,猜測漏洞點為Linux下read的第一參數(shù)可控,查閱資料可得(以下內容摘自https://www.cnblogs.com/xiehongfeng100/p/4619451.html):
在Linux下read函數(shù)定義如下:
#include <unistd>
ssize_t read(int filedes, void *buf, size_t nbytes);
// 返回:若成功則返回讀到的字節(jié)數(shù),若已到文件末尾則返回0,若出錯則返回-1
// filedes:文件描述符[0-標準輸入(stdin),1-標準輸出(stdout),2-標準錯誤輸出(stderr)]
// buf:讀取數(shù)據緩存區(qū)
// nbytes:要讀取的字節(jié)數(shù)
有幾種情況可使實際讀到的字節(jié)數(shù)少于要求讀的字節(jié)數(shù):
1)讀普通文件時,在讀到要求字節(jié)數(shù)之前就已經達到了文件末端。例如,若在到達文件末端之前還有30個字節(jié),而要求讀100個字節(jié),則read返回30,下一次再調用read時,它將返回0(文件末端)。
2)當從終端設備讀時,通常一次最多讀一行。
3)當從網絡讀時,網絡中的緩存機構可能造成返回值小于所要求讀的字結束。
4)當從管道或FIFO讀時,如若管道包含的字節(jié)少于所需的數(shù)量,那么read將只返回實際可用的字節(jié)數(shù)。
5)當從某些面向記錄的設備(例如磁帶)讀時,一次最多返回一個記錄。
6)當某一個信號造成中斷,而已經讀取了部分數(shù)據。
在《UNIX網絡編程 卷1》中,作者將該函數(shù)進行了封裝,以確保數(shù)據讀取的完整,具體程序如下:
1 ssize_t /* Read "n" bytes from a descriptor. */
2 readn(int fd, void *vptr, size_t n)
3 {
4 size_t nleft;
5 ssize_t nread;
6 char *ptr;
7
8 ptr = vptr;
9 nleft = n;
10 while (nleft > 0) {
11 if ( (nread = read(fd, ptr, nleft)) < 0) {
12 if (errno == EINTR)
13 nread = 0; /* and call read() again */
14 else
15 return(-1);
16 } else if (nread == 0)
17 break; /* EOF */
18
19 nleft -= nread;
20 ptr += nread;
21 }
22 return(n - nleft); /* return >= 0 */
23 }
24 /* end readn */
25
26 ssize_t
27 Readn(int fd, void *ptr, size_t nbytes)
28 {
29 ssize_t n;
30
31 if ( (n = readn(fd, ptr, nbytes)) < 0)
32 err_sys("readn error");
33 return(n);
34 }
因此,我們只需要使得read函數(shù)的filedes(文件描述符)為0即可對buf變量進行最大32字節(jié)的填充,這里只需要填充"LETMEWIN"即可順利cat flag。
題目二(collision - 3 pt )
與第一題一樣,看文件,讀源碼,嘗試無參運行。
col@ubuntu:~$ ls
col col.c flag
col@ubuntu:~$ 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;
}
col@ubuntu:~$ ./col
usage : ./col [passcode]
這個程序的邏輯相對簡單,即為輸入20字節(jié)長度的參數(shù),經過check_password的變換,輸出int變量與0x21DD09EC比較,相等即輸出flag。
為了說明數(shù)據在內存中的存儲方式,在這里我們用一個簡單的方式給予說明。
#include<bits/stdc++.h>
using namespace std;
int main()
{
char str[20]="ABCDEFGHIJKLMNOPQRS";
int *p=(int *)str;
for(int i=0; i<5; i++)
cout<<p[i]<<endl;
return 0;
}
發(fā)現(xiàn)輸出
1145258561
1212630597
1280002633
1347374669
5460561
--------------------------------
Process exited with return value 0
Press any key to continue . . .
轉換為16進制發(fā)現(xiàn)即為
0x44434241
0x48474645
0x4C4B4A49
0x504F4E4D
0x535251
那么嘗試
#include<bits/stdc++.h>
using namespace std;
int main()
{
char str[20]={'\x01','\x01','\x01','\x01','\x01','\x01','\x01','\x01','\x01','\x01','\x01','\x01','\x01','\x01','\x01','\x01','\x01','\x01','\x01','\x00'};
int *p=(int *)str;
for(int i=0; i<5; i++){
cout<<dec<<p[i]<<endl;
cout<<hex<<p[i]<<endl;
}
return 0;
}
果然結果為
16843009
1010101
16843009
1010101
16843009
1010101
16843009
1010101
65793
10101
--------------------------------
Process exited with return value 0
Press any key to continue . . .
那么我們嘗試構造payload。
0x21DD09EC=568134124 33686018*4=134744072 568134124-134744072=433390052
33686018=0x02020202 433390052=0x19D501E4
所以payload是'\xe4\x01\xd5\x19\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02'
實驗
#include<bits/stdc++.h>
using namespace std;
int main()
{
char str[20]={'\x19','\xd5','\x01','\xe4','\x02','\x02','\x02','\x02','\x02','\x02','\x02','\x02','\x02','\x02','\x02','\x02','\x02','\x02','\x02','\x02'};
int *p=(int *)str;
int tmp;
for(int i=0; i<5; i++)
tmp+=p[i];
cout<<dec<<tmp;
cout<<hex<<tmp;
return 0;
}
結果
433390052
33686018
33686018
33686018
33686018
568134124
21dd09ec
--------------------------------
Process exited with return value 0
Press any key to continue . . .
顯然正確,那么嘗試帶參數(shù)運行程序。
col@ubuntu:~$ ./col `python -c "print '\x02\x02\x02\x02'*4+'\xE4\x01\xD5\x19'"`
拿到flag。
題目三(bof - 5 pt )
等待更新~