PenetrationTestingScripts/antSword/source/modules/terminal/index.js

251 lines
7.4 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/**
* 虚拟终端模块
* 更新2016/04/13
* 作者:蚁逅 <https://github.com/antoor>
*/
const LANG = antSword['language']['terminal'];
const LANG_T = antSword['language']['toastr'];
class Terminal {
constructor(opts) {
// 生存一个随机ID用于标识多个窗口dom
const hash = String(Math.random()).substr(2, 10);
// 初始化UI::tabbar
const tabbar = antSword['tabbar'];
tabbar.addTab(
`tab_terminal_${hash}`,
`<i class="fa fa-terminal"></i> ${opts['ip']}`,
null, null, true, true
);
tabbar.attachEvent('onTabClick', (id,lid) => {
if (id !== `tab_terminal_${hash}`) { return };
this.term ? this.term.focus() : 0;
});
// 初始化UI::cell
const cell = tabbar.cells(`tab_terminal_${hash}`);
cell.attachHTMLString(`
<div
id="div_terminal_${hash}"
style="height:100%;margin:0;padding:0 5px 1px 5px;overflow:scroll"
></div>
`);
this.path = '';
this.opts = opts;
this.hash = hash;
this.term = null;
this.cell = cell;
this.isWin = true;
this.core = new antSword['core'][opts['type']](opts);
this.cache = new antSword['CacheManager'](this.opts['_id']);
this
.getInformation()
.then((ret) => {
this.initTerminal(ret['info'], ret['dom']);
})
.catch((err) => {
toastr.error((typeof(err) === 'object') ? JSON.stringify(err) : String(err), LANG_T['error']);
this.cell.progressOff();
this.cell.close();
})
}
/**
* 获取目标信息
* @return {Promise} 返回一个Promise操作对象//{dom,info}
*/
getInformation() {
return new Promise((ret, rej) => {
// 获取DOM
const dom = $(`#div_terminal_${this.hash}`);
// 获取缓存
let infoCache = this.cache.get('info');
// 如果有缓存?初始化终端:获取信息&&保存缓存&&初始化终端
if (infoCache) {
return ret({
dom: dom,
info: infoCache
});
}
// 开始获取信息
this.cell.progressOn();
this.core.request(
this.core.base.info()
).then((_ret) => {
this.cell.progressOff();
this.cache.set('info', _ret['text']);
return ret({
dom: dom,
info: _ret['text']
});
}).catch((e) => {
rej(e);
});
});
}
/**
* 初始化终端
* @param {String} ret 目标信息
* @param {Object} dom 要操作的dom元素
* @return {None} [description]
*/
initTerminal(ret, dom) {
let info = ret.split('\t');
let infoUser, infoPath, infoDrive, infoSystem;
let banner = `[[b;cyan;](*) ${LANG['banner']['title']}]`;
// 判断数据是否正确
if (info.length === 4) {
infoPath = info[0];
infoDrive = info[1];
infoSystem = info[2];
infoUser = info[3];
} else if (info.length === 2) {
infoPath = info[0];
infoDrive = info[1];
} else {
toastr.error('Loading infomations failed!<br/>' + ret, LANG_T['error']);
this.cache.del('info');
return this.cell.close();
}
// 转换路径特殊字符
infoPath = infoPath.replace(/\\/g, '/').replace(/\.$/, '');
// 判断是否为!win
this.isWin = !(infoPath.substr(0, 1) === '/')
this.path = infoPath;
// 组合banner
banner += `\n[[b;#99A50D;]${LANG['banner']['path']}]: [[;#C3C3C3;]${infoPath}]`;
banner += `\n[[b;#99A50D;]${LANG['banner']['drive']}]: [[;#C3C3C3;]${infoDrive}]`;
if (info.length === 4) {
banner += `\n[[b;#99A50D;]${LANG['banner']['system']}]: [[;#C3C3C3;]${infoSystem}]`;
banner += `\n[[b;#99A50D;]${LANG['banner']['user']}]: [[;#C3C3C3;]${infoUser}]`;
}
// 初始化终端
this.term = dom.terminal( (cmd, term) => {
if (!cmd) { return false }
// 如果为exit||quit则关闭窗口
if (cmd === 'exit' || cmd === 'quit') { return this.cell.close() }
// clear清空
if (cmd === 'cls' || cmd === 'clear') { return term.clear() }
term.pause();
// 是否有缓存
let cacheTag = 'command-' + new Buffer(this.path + cmd).toString('base64');
let cacheCmd = this.cache.get(cacheTag);
if (
(this.opts.otherConf || {})['terminal-cache'] === 1 && cacheCmd
) {
term.echo(
antSword.noxss(cacheCmd, false)
);
return term.resume();
};
// 获取自定义执行路径
let _bin = this.isWin ? 'cmd' : '/bin/sh';
let _confBin = (this.opts['otherConf'] || {})['command-path'];
_bin = _confBin || _bin;
// 开始执行命令
this.core.request(
this.core.command.exec({
cmd: this.parseCmd(cmd, this.path),
bin: _bin
})
).then((ret) => {
let _ = ret['text'];
// 解析出命令执行路径
const indexS = _.lastIndexOf('[S]');
const indexE = _.lastIndexOf('[E]');
let _path = _.substr(indexS + 3, indexE - indexS - 3);
let output = _.replace(`[S]${_path}[E]`, '');
_path = _path.replace(/\n/g, '').replace(/\r/g, '');
this.path = _path || this.path;
term.set_prompt(this.parsePrompt(infoUser));
// 去除换行符
[
/\n\n$/, /^\n\n/, /\r\r$/,
/^\r\r/, /\r\n$/, /^\r\n/,
/\n\r$/, /^\n\r/, /\r$/,
/^\r/, /\n$/, /^\n/
].map((_) => {
output = output.replace(_, '');
});
if (output.length > 0) {
term.echo(
antSword.noxss(output, false)
);
// 保存最大100kb数据
if (output.length < (1024 * 1024)) {
this.cache.set(cacheTag, output);
};
};
term.resume();
}).catch((_) => {
// term.error('ERR: ' + (_ instanceof Object) ? JSON.stringify(_) : String(_));
term.resume();
});
}, {
greetings: banner,
name: `terminal_${this.hash}`,
prompt: this.parsePrompt(infoUser),
numChars: 2048,
exit: false,
// < 1.0.0 时使用3个参数 completion: (term, value, callback) => {}
completion: (value, callback) => {
callback(
this.isWin ? [
'dir', 'whoami', 'net', 'ipconfig', 'netstat', 'cls',
'wscript', 'nslookup', 'copy', 'del', 'ren', 'md', 'type',
'ping'
] : [
'cd', 'ls', 'find', 'cp', 'mv', 'rm', 'ps', 'kill',
'file', 'tar', 'cat', 'chown', 'chmod', 'pwd', 'history',
'whoami', 'ifconfig', 'clear',
'ping'
]
)
}
});
}
/**
* 根据执行命令&&路径生成最终command
* @param {String} cmd 要执行的命令
* @param {String} path 当前路径
* @return {String} 最终执行命令
*/
parseCmd(cmd, path) {
path = path.replace(/\\\\/g, '\\').replace(/"/g, '\\"').replace(/\\/g, '\\\\');
return (this.isWin
? `cd /d "${path}"&${cmd}&echo [S]&cd&echo [E]`
: `cd "${path}";${cmd};echo [S];pwd;echo [E]`
);
}
/**
* 生成路径提示符
* @param {String} user 当前用户名
* @return {String} term输出字符串
*/
parsePrompt(user) {
return antSword.noxss(this.isWin
? `[[b;white;]${this.path.replace(/\//g, '\\')}> ]`
: (user ? `([[b;#E80000;]${user}]:[[;#0F93D2;]` : '[[;0F93D2;]') + this.path + ']) $ '
);
}
}
// export default Terminal;
module.exports = Terminal;