Skip to content

Commit fe38660

Browse files
authoredMar 1, 2020
Merge pull request #24 from cybersecmoo/feature/terminal
Feature/terminal
2 parents f8d076e + 44e020d commit fe38660

10 files changed

+226
-37
lines changed
 

‎package-lock.json

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "brownie-tub",
3-
"version": "0.4.0",
3+
"version": "0.5.0",
44
"main": "public/electron.js",
55
"private": true,
66
"author": "cybersecmoo <50770700+cybersecmoo@users.noreply.github.com>",

‎public/electron.js

+19-5
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
const { app, BrowserWindow, ipcMain } = require("electron");
33
const path = require("path");
44
const { getDatabase } = require("./db/setup-db");
5-
const { sendRequest, determineOS, listDir, workingDir } = require("./utils/requests");
5+
const { sendArbitraryCommand, determineOS, listDir, workingDir } = require("./utils/requests");
6+
const { parseMultiline } = require("./utils/utils");
67

78

89

@@ -27,6 +28,7 @@ async function createWindow() {
2728

2829
const db = await getDatabase("shells", "websql");
2930
const shellCollection = db.shells;
31+
var selectedShell = null;
3032

3133
ipcMain.on("shell:create", async (event, shellDetails) => {
3234
try {
@@ -64,17 +66,29 @@ async function createWindow() {
6466

6567
ipcMain.on("shell:select", async (event, shellDetails) => {
6668
try {
69+
selectedShell = shellDetails;
6770
// TODO: Admin determination
68-
shellDetails.os = await determineOS(shellDetails);
69-
const dir = await listDir(shellDetails);
70-
const dirName = await workingDir(shellDetails);
71-
event.reply("shell:select-reply", { shell: shellDetails, dir: dir, dirName: dirName });
71+
selectedShell.os = await determineOS(selectedShell);
72+
const dir = await listDir(selectedShell);
73+
const dirName = await workingDir(selectedShell);
74+
event.reply("shell:select-reply", { shell: selectedShell, dir: dir, dirName: dirName });
7275
} catch (error) {
7376
console.error(error);
7477
event.reply("misc:alert", {alertType: "warning", alertMessage: "Failed to load shell details!"});
7578
}
7679
});
7780

81+
ipcMain.on("terminal:command", async (event, command) => {
82+
try {
83+
var output = await sendArbitraryCommand(selectedShell, command);
84+
output = parseMultiline(output);
85+
event.reply("terminal:command-reply", output);
86+
} catch(err) {
87+
console.log(err);
88+
event.reply("misc:alert", {alertType: "warning", alertMessage: "Failed to send command!"});
89+
}
90+
});
91+
7892
// Listen for window being closed
7993
mainWindow.on("closed", () => {
8094
mainWindow = null;

‎public/utils/requests.js

+45-10
Original file line numberDiff line numberDiff line change
@@ -73,29 +73,58 @@ const encodeCommand = (command, shellEncoding) => {
7373
return encoded;
7474
}
7575

76+
/**
77+
* Sends a predefined request type
78+
*
79+
* @param {WebShellSchema} shell The details of the selected shell
80+
* @param {String} reqType The name of the request type, as defined in `reqTypes.js`
81+
* @exports
82+
*/
7683
const sendRequest = async (shell, reqType) => {
7784
var command = COMMAND_MAP[reqType][shell.os];
78-
command = encodeCommand(command, shell.commandEncoding);
85+
return sendArbitraryCommand(shell, command);
86+
}
7987

80-
const config = generateConfig(shell, command);
81-
var response;
88+
/** @exports */
89+
const sendArbitraryCommand = async (shell, command) => {
90+
try {
91+
var request = encodeCommand(command, shell.commandEncoding);
8292

83-
if(shell.commandParamType === "POST") {
84-
response = await axios.post(shell.ipOrHostname, config);
85-
} else {
86-
response = await axios.get(shell.ipOrHostname, config);
87-
}
93+
const config = generateConfig(shell, request);
94+
var response;
95+
96+
if(shell.commandParamType === "POST") {
97+
response = await axios.post(shell.ipOrHostname, config);
98+
} else {
99+
response = await axios.get(shell.ipOrHostname, config);
100+
}
88101

89-
return response;
102+
return response;
103+
} catch (error) {
104+
return null;
105+
}
106+
90107
}
91108

109+
/**
110+
* Gets the directory listing from the remote, and parses it
111+
*
112+
* @param {WebShellSchema} shell The details of the selected shell
113+
* @exports
114+
*/
92115
const listDir = async (shell) => {
93116
const response = await sendRequest(shell, LIST_DIR);
94117
const dir = parseListDirResponse(response.data);
95118

96119
return dir;
97120
}
98121

122+
/**
123+
* Determines what operating system the target is running
124+
*
125+
* @param {WebShellSchema} shell The details of the selected shell
126+
* @exports
127+
*/
99128
const determineOS = async (shell) => {
100129
const command = encodeCommand("uname -a", shell.commandEncoding);
101130

@@ -122,11 +151,17 @@ const determineOS = async (shell) => {
122151
return os;
123152
};
124153

154+
/**
155+
* Gets the current working directory of the remote
156+
*
157+
* @param {WebShellSchema} shell The details of the selected shell
158+
* @exports
159+
*/
125160
const workingDir = async (shell) => {
126161
const response = await sendRequest(shell, WORKING_DIR);
127162
const dirName = parseWorkingDir(response.data);
128163

129164
return dirName;
130165
};
131166

132-
module.exports = { sendRequest, determineOS, listDir, workingDir };
167+
module.exports = { sendRequest, sendArbitraryCommand, determineOS, listDir, workingDir };

‎public/utils/utils.js

+28-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,18 @@
11
const { WINDOWS, LINUX, MAC } = require("./osTypes");
22

3+
/**
4+
* Uses a regex to split the input into its component lines
5+
*
6+
* @param {String} multilineInput The data to split
7+
* @exports
8+
*/
9+
const parseMultiline = (multilineInput) => {
10+
const lines = multilineInput.split(/\r?\n/);
11+
return lines;
12+
}
13+
314
const parseWindowsListDir = (listDirResponse) => {
4-
const lines = listDirResponse.split(/\r?\n/);
15+
const lines = parseMultiline(listDirResponse);
516
var dir = [];
617

718
for(const lineNo in lines) {
@@ -39,7 +50,7 @@ const parseWindowsListDir = (listDirResponse) => {
3950
// NOTE This relies on parsing the output of `ls -la`, and is therefore not 100% reliable against crafted filenames. However, it should be fine against the vast
4051
// majority of files. Just be aware.
4152
const parseUnixListDir = (listDirResponse) => {
42-
const lines = listDirResponse.split(/\r?\n/);
53+
const lines = parseMultiline(listDirResponse);
4354
var dir = [];
4455

4556
for(const lineNo in lines) {
@@ -73,6 +84,13 @@ const parseUnixListDir = (listDirResponse) => {
7384
return dir;
7485
}
7586

87+
/**
88+
* Parses a directory listing, for Windows and Unix platforms
89+
*
90+
* @param {Object} listDirResponse The output of the directory listing command
91+
* @param {String} os The operating system used on the target
92+
* @exports
93+
*/
7694
const parseListDirResponse = (listDirResponse, os) => {
7795
var dir;
7896

@@ -85,9 +103,15 @@ const parseListDirResponse = (listDirResponse, os) => {
85103
return dir;
86104
}
87105

106+
/**
107+
* Parses the output of a working-directory request
108+
*
109+
* @param {Object} workingDirResponse The output of the request to get the working directory
110+
* @exports
111+
*/
88112
const parseWorkingDir = (workingDirResponse) => {
89-
const lines = workingDirResponse.split(/\r?\n/);
113+
const lines = parseMultiline(workingDirResponse);
90114
return lines[0];
91115
}
92116

93-
module.exports = { parseListDirResponse, parseWorkingDir };
117+
module.exports = { parseMultiline, parseListDirResponse, parseWorkingDir };

‎src/App.css

+12
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,15 @@
3636
transform: rotate(360deg);
3737
}
3838
}
39+
40+
div.main {
41+
height: 100vh;
42+
}
43+
44+
.row1 {
45+
height: 67vh;
46+
}
47+
48+
.row2 {
49+
height: 33vh;
50+
}

‎src/App.js

+12-2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import ShellList from "./components/shell-list.component";
99
import ShellDetailPanel from "./components/shell-detail-panel.component";
1010
import DirectoryView from "./components/directory-view.component";
1111
import { Component } from 'react';
12+
import Terminal from "./components/terminal.component";
1213

1314
const dark = createMuiTheme({
1415
palette: {
@@ -39,8 +40,8 @@ class App extends Component {
3940
return (
4041
<ThemeProvider theme={dark}>
4142
<CssBaseline />
42-
<div>
43-
<Grid container>
43+
<div className="main">
44+
<Grid container className="row1">
4445
<Grid item xs={2}>
4546
<ShellList />
4647
</Grid>
@@ -51,6 +52,15 @@ class App extends Component {
5152
<ShellDetailPanel />
5253
</Grid>
5354
</Grid>
55+
<Grid container className="row2">
56+
<Grid item xs={2}>
57+
</Grid>
58+
<Grid item xs={8}>
59+
<Terminal />
60+
</Grid>
61+
<Grid item xs={2}>
62+
</Grid>
63+
</Grid>
5464
<Snackbar open={this.state.showAlert} autoHideDuration={6000} onClose={this.handleAlertClose}>
5565
<Alert severity={this.state.alertType} onClose={this.handleAlertClose}>
5666
{this.state.alertMessage}

‎src/components/shell-create-form.component.jsx

+9-14
Original file line numberDiff line numberDiff line change
@@ -54,20 +54,19 @@ class ShellCreateForm extends Component {
5454
<DialogContent>
5555
<DialogTitle>Register a New Shell</DialogTitle>
5656
<div>
57-
<FormControl>
58-
<TextField required autoFocus id="ipOrHostname" label="IP or URL" onChange={this.handleStringChanged("ipOrHostname")} fullWidth />
59-
</FormControl>
57+
<TextField required autoFocus size="small" id="ipOrHostname" label="IP or URL" onChange={this.handleStringChanged("ipOrHostname")} fullWidth />
6058
</div>
6159

6260
<div>
6361
<FormControl>
64-
<InputLabel required id="commandParamType-label">Param Type</InputLabel>
62+
<InputLabel required size="small" id="commandParamType-label">Param Type</InputLabel>
6563
<Select
6664
required
6765
labelId="commandParamType-label"
6866
id="commandParamType"
6967
value={this.state.paramType}
7068
onChange={this.handleStringChanged("commandParamType")}
69+
size="small"
7170
>
7271
<MenuItem value={"header"}>Header</MenuItem>
7372
<MenuItem value={"cookie"}>Cookie</MenuItem>
@@ -83,18 +82,17 @@ class ShellCreateForm extends Component {
8382
id="commandEncoding"
8483
value={this.state.paramType}
8584
onChange={this.handleStringChanged("commandEncoding")}
85+
size="small"
8686
>
8787
<MenuItem value="None">None</MenuItem>
8888
<MenuItem value={"base64"}>Base64</MenuItem>
8989
</Select>
9090
</FormControl>
91-
<FormControl>
92-
<TextField required id="commandParam" label="Command Parameter" onChange={this.handleStringChanged("commandParam")} fullWidth />
93-
</FormControl>
91+
<TextField required id="commandParam" size="small" label="Command Parameter" onChange={this.handleStringChanged("commandParam")} fullWidth />
9492
</div>
9593

9694
<div>
97-
<FormControlLabel control={
95+
<FormControlLabel size="small" control={
9896
<Checkbox checked={this.state.passwordEnabled} onChange={this.handleCheckboxChanged("passwordEnabled")} value="passwordEnabled" />
9997
}
10098
label="Enable Password" />
@@ -108,6 +106,7 @@ class ShellCreateForm extends Component {
108106
id="passwordParamType"
109107
value={this.state.passwordParamType}
110108
onChange={this.handleStringChanged("passwordParamType")}
109+
size="small"
111110
>
112111
<MenuItem selected value={"none"}>None</MenuItem>
113112
<MenuItem value={"header"}>Header</MenuItem>
@@ -116,12 +115,8 @@ class ShellCreateForm extends Component {
116115
<MenuItem value={"GET"}>GET Param</MenuItem>
117116
</Select>
118117
</FormControl>
119-
<FormControl>
120-
<TextField id="passwordParam" label="Password Parameter" onChange={this.handleStringChanged("passwordParam")} fullWidth />
121-
</FormControl>
122-
<FormControl>
123-
<TextField id="password" label="Password" onChange={this.handleStringChanged("password")} fullWidth />
124-
</FormControl>
118+
<TextField id="passwordParam" label="Password Parameter" size="small" onChange={this.handleStringChanged("passwordParam")} fullWidth />
119+
<TextField id="password" label="Password" size="small" onChange={this.handleStringChanged("password")} fullWidth />
125120
</div>
126121
</DialogContent>
127122
<DialogActions>

0 commit comments

Comments
 (0)