ctfshow web php部分有意思的题目

发布于 24 天前  104 次阅读


历时好久的 php

中间拖了丢了不想学了

终于是把整个板块拉完了

丢几道觉得还不错的题目又水一篇

web 109(无参数函数执行”漏洞利用链)

攻击者可以利用 PHP 内置函数或已声明的类实现以下目的:
任意代码执行$v1 = "assert"; $v2 = "system"(导致命令执行)
敏感信息泄露$v1 = "Exception"; $v2 = "get_defined_constants"(抛出异常时可能回显常量信息。)
文件操作$v1 = "SplFileObject"; $v2 = "fopen"(读取或写入服务器文件)
触发魔术方法链$v1 = "SimpleXMLElement"; $v2 = "file_get_contents"(通过 echo 触发 __toString() 进行 XXE 攻击或数据外带。)

eval("echo new $v1($v2());");

简易理解:v1=PDO,v2=phpinfo
执行的代码结果就是

echo new PDO( phpinfo() );


通过遍历路径找到flag文件,cat拿下flag

web 115

trim() 函数的作用是去除字符串首尾的空白字符
filter()函数用于验证和过滤外部输入数据
is_numeric() 函数用于检查变量是否为数字或数字字符串
url编码手册
这里使用%0C换页符

web 123(非法参数解析)

只要变量名中出现非法参数名(”空格“、”.“、”+“、“[")
就是这类型题
但php中有个特性就是如果传入【,它被转化为_之后,后面的字符就会被保留下来不会被替换

web 125

同上题非法参数解析
but此题ban掉了flag,所以没办法echo直接调用flag
我尝试使用system读取也失败了(说是底层逻辑就不给system写入)
直接使用include/highlight_file(only读取作用)读取成功

web 130

这题的正则有一个非贪婪模式(正则非贪婪模式是指在匹配时尽可能少地匹配字符,通常通过在量词后添加问号(?)来实现。)
也就是说

if(preg_match('/.+?ctfshow/is', $f)){  
        die('bye!');  
    }

"ctfshow"之前加任何东西都会被拦

if(stripos($f, 'ctfshow') === FALSE){  
        die('bye!!');  
    }

传入的f中必须带有“ctfshow”,否则触发bye
所以,直接尝试POST传入f=ctfshow/../../../var/www/html/flag.php
成功拿到flag
。。。
或者直接传入f=ctfshow
也能拿到
**原因:第一个if语句中无法匹配,第二个if语句中返回0,两个die都不会执行,会执行echo $flag;

web131(正则表达式栈溢出)

简单说,输入大量垃圾函数让这个函数承载量爆炸,使其返回 false,就会执行echo $flag;

<?php  
  
// 设置生成字符串的长度  
$length = 1000000;  
  
// 生成包含一百万个数字 "1" 的字符串  
$onesString = str_repeat('1', $length);  
  
// 输出结果  
echo $onesString;  
  
?>

很无聊的题目。。。。被气笑了,但也是给了一个新的解题思路

web 139(反弹shell 时间盲注)

开题

有一个解题方法是用时间盲注去解,但是解不出来
所以我们换反弹shell
思路在于用’'或者\来绕过命令的过滤,然后利用base64外带数据来找flag
开个监听


首先 . 被ban了 找个在线ip转换工具(上有政策下有对策)


发现绕过成功 那就ls一下看看有什么东西(因为是开的nc 这里有个很重要的点,每一次都要重新再开监听)

!

得到经过base64编码的字符串


看到f149_15_h3r3
cat一下

解码得到flag

web 137(无参数调用类静态方法)

call_user_func 把第一个参数作为回调函数调用

危险点在于用户可以完全控制 $callback 参数:

  • 可以调用任意已定义的函数
  • 可以调用任意类的静态方法(绕过对象实例化)
  • 不需要 new 创建对象,也就不触发 __wakeup() 等魔术方法
<?php  
function now($a,$b)  
{  
    echo $a;  
    echo $b;  
    echo $a+$b;  
  
}  
call_user_func('now', " 111 "," 222 ");  
call_user_func('now', " 333 "," 444 ");  
  
//显示 111 222 333
// 333 444 777  
function hello() {  
    echo "Hello World";  
}  
call_user_func('hello');  // 输出: Hello World


payload为ctfshow=ctfshow::getFlag
类的静态方式:静态方法是属于类本身而不是属于某个对象实例的方法
格式:call_user_func('Flag::getFlag');
类名::方法名()

web 138(利用数组调用静态方法)

同类型题,只不过这题用的是数组调用
格式:call_user_func([类名, 方法名]);
['类名', '方法名'] 等价于 ‘类名::方法名’
ctfshow[]=ctfshow&ctfshow[]=getFlag


payload为ctfshow[]=ctfshow&ctfshow[]=getFlag

147 (PHP 可变函数调用导致的动态代码执行)

全局命名空间

没有定义任何命名空间时的默认空间,用 \ 表示。

  • 如果某个函数在自定义命名空间被重写或禁用
  • 可以用 \函数名 直接调用全局命名空间中的原始系统函数
  • 从而绕过限制

核心:可变函数调用 + create_function() 代码注入 + 反斜杠绕过正则

if(!preg_match('/^[a-z0-9_]*$/isD',$ctfshow)) {
  • i:不区分大小写
  • s:点号 . 匹配包括换行符在内的所有字符
  • D:美元符号 $ 仅匹配字符串末尾,不匹配末尾的换行符
    意思:如果 ctf 不是纯字母数字下划线,就执行函数调用
    payload
GET: ?show=;};system('grep flag flag.php');/*
POST: ctf=%5ccreate_function

%5c解码后是
其中 \ 不在 [a-z0-9_] 范围内,所以正则匹配失败,preg_match() 返回 0,取反后进入 if

$ctfshow('',$_GET['show']);

这句代码的意思是:把$ctfshow里的内容当作函数名,然后调用这个函数,并传入两个参数
比如

$ctfshow = 'system';
$ctfshow('', $_GET['show']);

等价于

system('', $_GET['show']);

在这里:$ctfshow的值是post传入的‘ctf’,所以 POST 里的 ctf 控制了要调用哪个函数。

而在 PHP 里,函数名前面的反斜杠表示全局命名空间\create_function等价于调用全局函数:create_function
所以这一步既绕过了正则,又没有改变函数含义.
所以post传入的%5ccreate_function最终变为\create_function('', $_GET['show']);

为什么要用这个函数呢

create_function() 它会根据字符串动态创建函数,本质上类似于拼接代码再 eval
且 因为create_function函数格式为:
create_function(函数参数列表, 函数体代码)
所以create_function('', $_GET['show']);
意思就是创建一个“没有参数”的匿名函数,函数体代码由 show 参数控制

因此如果:

GET: show=echo 123;

大致相当于创建:

function 临时函数名() {
    echo 123;
}

但如果传:

GET: show=;};system('grep flag flag.php');/*

就可以把函数体闭合掉,逃出去执行语句:

system('grep flag flag.php');

详情看未命名php_ctf_create_function_analysis
看不懂 我已急哭

!!!!茅塞顿开

为什么这里不能用其他函数,比如直接system读取,要绕这么大个圈子呢

因为:
如果payload为

POST: ctf=\system
GET: show=cat flag.php

那解析后就变成了

system('', $_GET['show']);

对于system来说,system() 的第一个参数才是命令,而这里第一个参数被固定成了空字符串 '',所以不能直接执行我们想要的命令。

web149(条件竞争)

写入后清理,通常出现在高难度的 CTF 无参数 RCE(远程代码执行)
unlink() 是 PHP 的内置函数,专门用来删除服务器上的文件,功能等同于 Linux 的 rm 命令。

![[Pasted image 20260510203614.png]]

$files = scandir('./');   
foreach($files as $file) {  
    if(is_file($file)){  
        if ($file !== "index.php") {            unlink($file);  
        }  
    }  
}
  • 扫描当前目录。
  • 只保留 index.php 自身
  • 删除其他所有文件,确保目录干净,不给攻击者留下任何可利用的现成脚本
file_put_contents($_GET['ctf'], $_POST['show']);  
  
$files = scandir('./');   
foreach($files as $file) {  
    if(is_file($file)){  
        if ($file !== "index.php") {            unlink($file);  
        }  
    }  
}
  • 写入文件后,再次执行相同的清理逻辑

php文件进行写马操作

get重写index.php
post写一个一句话木马
哥斯拉连接


咔咔咔咔咔咔嚓