WEB40 无参RCE
web40
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-04 00:12:34
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-04 06:03:36
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
审计发现:禁用了0-9 之间的数字、~、反引号、@、#、$、%、^、&、*、中文的左括号、中文的右括号、-、=、+、{、[、]、}、英文冒号、单引号、双引号、逗号、<、>、/、?、\
方法1:
?c=show_source(next(array_reverse(scandir(pos(localeconv())))));
步骤分解:
localeconv()
返回包含本地化设置的数组,第一个元素通常是小数点字符.(当前目录)。
pos()
别名current(),获取数组第一个元素,即.。
scandir('.')
列出当前目录下的所有文件和目录,按字母升序排列,如['.', '..', 'file1', 'file2']。
array_reverse()
反转数组顺序,变为['file2', 'file1', '..', '.']。
next()
将数组指针从第一个元素移动到第二个,并返回其值。例如,反转后数组为['file2', 'file1', '..', '.'],next()返回'file1'。
show_source()
显示指定文件的源代码(语法高亮),若文件存在则输出内容,否则报错。
当前题目环境分布解析:
- localeconv()
返回本地化信息数组,默认第一个元素是小数点 .(当前目录)。
输出:['.', …] - pos(localeconv())
pos() 获取数组第一个元素,即 .。
输出:'.' - scandir('.')
扫描当前目录,返回按字母升序排列的文件列表:
输出:['.', '..', 'flag.php', 'index.php']
(flag.php 在 index.php 前,因为字母 f < i) - array_reverse()
反转数组顺序,得到:
输出:['index.php', 'flag.php', '..', '.'] - next()
初始指针指向第一个元素 index.php。调用 next() 后,指针移动到第二个元素 flag.php,并返回该值。输出:'flag.php' - show_source('flag.php')
方法2(此种方法仅为了解,无法获取到flag):
?c=session_start();system(session_id());
如下图,修改PHPSESSID为要执行的命令。由于PHPSESSID仅允许字符:a-z A-Z 0-9 , -(正则规则:[0-9a-zA-Z,-]),且长度通常限制为32或26字符,所有无法获取到flag。

session_start()
功能:启动或恢复PHP会话。
检查请求中是否存在有效的会话ID(通常通过PHPSESSID Cookie)。
若无有效ID,生成新的会话ID并发送Set-Cookie头。
system(session_id())
session_id()
:返回当前会话的ID(字符串形式)。system()
:执行系统命令并输出结果。- 组合逻辑:
- 将会话ID(
PHPSESSID
)作为命令传递给system()
执行。 - 攻击者通过篡改
PHPSESSID
的值注入恶意命令。
- 将会话ID(
攻击原理
- 控制会话ID:
- 攻击者设置Cookie头:
PHPSESSID=恶意命令
。 - 例如:
PHPSESSID=ls
。
- 攻击者设置Cookie头:
- 触发Payload:
- 访问URL:
?c=session_start();system(session_id());
。 session_start()
读取攻击者设定的PHPSESSID
。system(session_id())
执行PHPSESSID
中的命令(如ls
)
- 访问URL:
为什么需要session_start()
session_start()
是PHP会话管理的核心函数,其作用不仅仅是“启动会话”,更关键的是 将会话数据与当前请求绑定。以下是必须包含 session_start()
的原因:
1. 会话ID的读取依赖 session_start()
- PHP默认不会自动读取客户端的
PHPSESSID
Cookie,除非显式调用session_start()
。 - 如果没有
session_start()
:session_id()
返回的是空字符串或上一次会话残留的ID。- 攻击者设置的恶意
PHPSESSID
Cookie 不会被PHP识别,因此无法注入命令。
2. 会话数据的初始化
session_start()
会:- 根据
PHPSESSID
加载或创建会话文件。 - 初始化
$_SESSION
数组。 - 将
session_id()
的值与当前请求关联。
- 根据
- 如果跳过这一步,
session_id()
的值与PHPSESSID
Cookie 完全无关,攻击者无法通过Cookie传递命令。
空空如也!